317 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			317 lines
		
	
	
		
			9.7 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- unittest/AST/MatchVerifier.h - AST unit test support ---------------===//
 | |
| //
 | |
| //                     The LLVM Compiler Infrastructure
 | |
| //
 | |
| // This file is distributed under the University of Illinois Open Source
 | |
| // License. See LICENSE.TXT for details.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| //
 | |
| //  Provides MatchVerifier, a base class to implement gtest matchers that
 | |
| //  verify things that can be matched on the AST.
 | |
| //
 | |
| //  Also implements matchers based on MatchVerifier:
 | |
| //  LocationVerifier and RangeVerifier to verify whether a matched node has
 | |
| //  the expected source location or source range.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #ifndef LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H
 | |
| #define LLVM_CLANG_UNITTESTS_AST_MATCHVERIFIER_H
 | |
| 
 | |
| #include "clang/AST/ASTContext.h"
 | |
| #include "clang/ASTMatchers/ASTMatchFinder.h"
 | |
| #include "clang/ASTMatchers/ASTMatchers.h"
 | |
| #include "clang/Tooling/Tooling.h"
 | |
| #include "gtest/gtest.h"
 | |
| 
 | |
| namespace clang {
 | |
| namespace ast_matchers {
 | |
| 
 | |
| enum Language { 
 | |
|     Lang_C,
 | |
|     Lang_C89,
 | |
|     Lang_CXX,
 | |
|     Lang_CXX11,
 | |
|     Lang_OpenCL,
 | |
|     Lang_OBJCXX
 | |
| };
 | |
| 
 | |
| /// \brief Base class for verifying some property of nodes found by a matcher.
 | |
| template <typename NodeType>
 | |
| class MatchVerifier : public MatchFinder::MatchCallback {
 | |
| public:
 | |
|   template <typename MatcherType>
 | |
|   testing::AssertionResult match(const std::string &Code,
 | |
|                                  const MatcherType &AMatcher) {
 | |
|     std::vector<std::string> Args;
 | |
|     return match(Code, AMatcher, Args, Lang_CXX);
 | |
|   }
 | |
| 
 | |
|   template <typename MatcherType>
 | |
|   testing::AssertionResult match(const std::string &Code,
 | |
|                                  const MatcherType &AMatcher,
 | |
|                                  Language L) {
 | |
|     std::vector<std::string> Args;
 | |
|     return match(Code, AMatcher, Args, L);
 | |
|   }
 | |
| 
 | |
|   template <typename MatcherType>
 | |
|   testing::AssertionResult match(const std::string &Code,
 | |
|                                  const MatcherType &AMatcher,
 | |
|                                  std::vector<std::string>& Args,
 | |
|                                  Language L);
 | |
| 
 | |
|   template <typename MatcherType>
 | |
|   testing::AssertionResult match(const Decl *D, const MatcherType &AMatcher);
 | |
| 
 | |
| protected:
 | |
|   void run(const MatchFinder::MatchResult &Result) override;
 | |
|   virtual void verify(const MatchFinder::MatchResult &Result,
 | |
|                       const NodeType &Node) {}
 | |
| 
 | |
|   void setFailure(const Twine &Result) {
 | |
|     Verified = false;
 | |
|     VerifyResult = Result.str();
 | |
|   }
 | |
| 
 | |
|   void setSuccess() {
 | |
|     Verified = true;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   bool Verified;
 | |
|   std::string VerifyResult;
 | |
| };
 | |
| 
 | |
| /// \brief Runs a matcher over some code, and returns the result of the
 | |
| /// verifier for the matched node.
 | |
| template <typename NodeType> template <typename MatcherType>
 | |
| testing::AssertionResult MatchVerifier<NodeType>::match(
 | |
|     const std::string &Code, const MatcherType &AMatcher,
 | |
|     std::vector<std::string>& Args, Language L) {
 | |
|   MatchFinder Finder;
 | |
|   Finder.addMatcher(AMatcher.bind(""), this);
 | |
|   std::unique_ptr<tooling::FrontendActionFactory> Factory(
 | |
|       tooling::newFrontendActionFactory(&Finder));
 | |
| 
 | |
|   StringRef FileName;
 | |
|   switch (L) {
 | |
|   case Lang_C:
 | |
|     Args.push_back("-std=c99");
 | |
|     FileName = "input.c";
 | |
|     break;
 | |
|   case Lang_C89:
 | |
|     Args.push_back("-std=c89");
 | |
|     FileName = "input.c";
 | |
|     break;
 | |
|   case Lang_CXX:
 | |
|     Args.push_back("-std=c++98");
 | |
|     FileName = "input.cc";
 | |
|     break;
 | |
|   case Lang_CXX11:
 | |
|     Args.push_back("-std=c++11");
 | |
|     FileName = "input.cc";
 | |
|     break;
 | |
|   case Lang_OpenCL:
 | |
|     FileName = "input.cl";
 | |
|     break;
 | |
|   case Lang_OBJCXX:
 | |
|     FileName = "input.mm";
 | |
|     break;
 | |
|   }
 | |
| 
 | |
|   // Default to failure in case callback is never called
 | |
|   setFailure("Could not find match");
 | |
|   if (!tooling::runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName))
 | |
|     return testing::AssertionFailure() << "Parsing error";
 | |
|   if (!Verified)
 | |
|     return testing::AssertionFailure() << VerifyResult;
 | |
|   return testing::AssertionSuccess();
 | |
| }
 | |
| 
 | |
| /// \brief Runs a matcher over some AST, and returns the result of the
 | |
| /// verifier for the matched node.
 | |
| template <typename NodeType> template <typename MatcherType>
 | |
| testing::AssertionResult MatchVerifier<NodeType>::match(
 | |
|     const Decl *D, const MatcherType &AMatcher) {
 | |
|   MatchFinder Finder;
 | |
|   Finder.addMatcher(AMatcher.bind(""), this);
 | |
| 
 | |
|   setFailure("Could not find match");
 | |
|   Finder.match(*D, D->getASTContext());
 | |
| 
 | |
|   if (!Verified)
 | |
|     return testing::AssertionFailure() << VerifyResult;
 | |
|   return testing::AssertionSuccess();
 | |
| }
 | |
| 
 | |
| template <typename NodeType>
 | |
| void MatchVerifier<NodeType>::run(const MatchFinder::MatchResult &Result) {
 | |
|   const NodeType *Node = Result.Nodes.getNodeAs<NodeType>("");
 | |
|   if (!Node) {
 | |
|     setFailure("Matched node has wrong type");
 | |
|   } else {
 | |
|     // Callback has been called, default to success.
 | |
|     setSuccess();
 | |
|     verify(Result, *Node);
 | |
|   }
 | |
| }
 | |
| 
 | |
| template <>
 | |
| inline void MatchVerifier<ast_type_traits::DynTypedNode>::run(
 | |
|     const MatchFinder::MatchResult &Result) {
 | |
|   BoundNodes::IDToNodeMap M = Result.Nodes.getMap();
 | |
|   BoundNodes::IDToNodeMap::const_iterator I = M.find("");
 | |
|   if (I == M.end()) {
 | |
|     setFailure("Node was not bound");
 | |
|   } else {
 | |
|     // Callback has been called, default to success.
 | |
|     setSuccess();
 | |
|     verify(Result, I->second);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /// \brief Verify whether a node has the correct source location.
 | |
| ///
 | |
| /// By default, Node.getSourceLocation() is checked. This can be changed
 | |
| /// by overriding getLocation().
 | |
| template <typename NodeType>
 | |
| class LocationVerifier : public MatchVerifier<NodeType> {
 | |
| public:
 | |
|   void expectLocation(unsigned Line, unsigned Column) {
 | |
|     ExpectLine = Line;
 | |
|     ExpectColumn = Column;
 | |
|   }
 | |
| 
 | |
| protected:
 | |
|   void verify(const MatchFinder::MatchResult &Result,
 | |
|               const NodeType &Node) override {
 | |
|     SourceLocation Loc = getLocation(Node);
 | |
|     unsigned Line = Result.SourceManager->getSpellingLineNumber(Loc);
 | |
|     unsigned Column = Result.SourceManager->getSpellingColumnNumber(Loc);
 | |
|     if (Line != ExpectLine || Column != ExpectColumn) {
 | |
|       std::string MsgStr;
 | |
|       llvm::raw_string_ostream Msg(MsgStr);
 | |
|       Msg << "Expected location <" << ExpectLine << ":" << ExpectColumn
 | |
|           << ">, found <";
 | |
|       Loc.print(Msg, *Result.SourceManager);
 | |
|       Msg << '>';
 | |
|       this->setFailure(Msg.str());
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   virtual SourceLocation getLocation(const NodeType &Node) {
 | |
|     return Node.getLocation();
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   unsigned ExpectLine, ExpectColumn;
 | |
| };
 | |
| 
 | |
| /// \brief Verify whether a node has the correct source range.
 | |
| ///
 | |
| /// By default, Node.getSourceRange() is checked. This can be changed
 | |
| /// by overriding getRange().
 | |
| template <typename NodeType>
 | |
| class RangeVerifier : public MatchVerifier<NodeType> {
 | |
| public:
 | |
|   void expectRange(unsigned BeginLine, unsigned BeginColumn,
 | |
|                    unsigned EndLine, unsigned EndColumn) {
 | |
|     ExpectBeginLine = BeginLine;
 | |
|     ExpectBeginColumn = BeginColumn;
 | |
|     ExpectEndLine = EndLine;
 | |
|     ExpectEndColumn = EndColumn;
 | |
|   }
 | |
| 
 | |
| protected:
 | |
|   void verify(const MatchFinder::MatchResult &Result,
 | |
|               const NodeType &Node) override {
 | |
|     SourceRange R = getRange(Node);
 | |
|     SourceLocation Begin = R.getBegin();
 | |
|     SourceLocation End = R.getEnd();
 | |
|     unsigned BeginLine = Result.SourceManager->getSpellingLineNumber(Begin);
 | |
|     unsigned BeginColumn = Result.SourceManager->getSpellingColumnNumber(Begin);
 | |
|     unsigned EndLine = Result.SourceManager->getSpellingLineNumber(End);
 | |
|     unsigned EndColumn = Result.SourceManager->getSpellingColumnNumber(End);
 | |
|     if (BeginLine != ExpectBeginLine || BeginColumn != ExpectBeginColumn ||
 | |
|         EndLine != ExpectEndLine || EndColumn != ExpectEndColumn) {
 | |
|       std::string MsgStr;
 | |
|       llvm::raw_string_ostream Msg(MsgStr);
 | |
|       Msg << "Expected range <" << ExpectBeginLine << ":" << ExpectBeginColumn
 | |
|           << '-' << ExpectEndLine << ":" << ExpectEndColumn << ">, found <";
 | |
|       Begin.print(Msg, *Result.SourceManager);
 | |
|       Msg << '-';
 | |
|       End.print(Msg, *Result.SourceManager);
 | |
|       Msg << '>';
 | |
|       this->setFailure(Msg.str());
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   virtual SourceRange getRange(const NodeType &Node) {
 | |
|     return Node.getSourceRange();
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   unsigned ExpectBeginLine, ExpectBeginColumn, ExpectEndLine, ExpectEndColumn;
 | |
| };
 | |
| 
 | |
| /// \brief Verify whether a node's dump contains a given substring.
 | |
| class DumpVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> {
 | |
| public:
 | |
|   void expectSubstring(const std::string &Str) {
 | |
|     ExpectSubstring = Str;
 | |
|   }
 | |
| 
 | |
| protected:
 | |
|   void verify(const MatchFinder::MatchResult &Result,
 | |
|               const ast_type_traits::DynTypedNode &Node) override {
 | |
|     std::string DumpStr;
 | |
|     llvm::raw_string_ostream Dump(DumpStr);
 | |
|     Node.dump(Dump, *Result.SourceManager);
 | |
| 
 | |
|     if (Dump.str().find(ExpectSubstring) == std::string::npos) {
 | |
|       std::string MsgStr;
 | |
|       llvm::raw_string_ostream Msg(MsgStr);
 | |
|       Msg << "Expected dump substring <" << ExpectSubstring << ">, found <"
 | |
|           << Dump.str() << '>';
 | |
|       this->setFailure(Msg.str());
 | |
|     }
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   std::string ExpectSubstring;
 | |
| };
 | |
| 
 | |
| /// \brief Verify whether a node's pretty print matches a given string.
 | |
| class PrintVerifier : public MatchVerifier<ast_type_traits::DynTypedNode> {
 | |
| public:
 | |
|   void expectString(const std::string &Str) {
 | |
|     ExpectString = Str;
 | |
|   }
 | |
| 
 | |
| protected:
 | |
|   void verify(const MatchFinder::MatchResult &Result,
 | |
|               const ast_type_traits::DynTypedNode &Node) override {
 | |
|     std::string PrintStr;
 | |
|     llvm::raw_string_ostream Print(PrintStr);
 | |
|     Node.print(Print, Result.Context->getPrintingPolicy());
 | |
| 
 | |
|     if (Print.str() != ExpectString) {
 | |
|       std::string MsgStr;
 | |
|       llvm::raw_string_ostream Msg(MsgStr);
 | |
|       Msg << "Expected pretty print <" << ExpectString << ">, found <"
 | |
|           << Print.str() << '>';
 | |
|       this->setFailure(Msg.str());
 | |
|     }
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   std::string ExpectString;
 | |
| };
 | |
| 
 | |
| } // end namespace ast_matchers
 | |
| } // end namespace clang
 | |
| 
 | |
| #endif
 |