Index: unittests/AST/CMakeLists.txt =================================================================== --- unittests/AST/CMakeLists.txt +++ unittests/AST/CMakeLists.txt @@ -2,6 +2,7 @@ CommentLexer.cpp CommentParser.cpp DeclPrinterTest.cpp + SourceLocationTest.cpp StmtPrinterTest.cpp ) Index: unittests/AST/SourceLocationTest.cpp =================================================================== --- /dev/null +++ unittests/AST/SourceLocationTest.cpp @@ -0,0 +1,198 @@ +//===- unittest/AST/SourceLocationTest.cpp - AST source loc unit tests ----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Tooling.h" +#include "gtest/gtest.h" + +namespace clang { +namespace ast_matchers { + +using clang::tooling::newFrontendActionFactory; +using clang::tooling::runToolOnCodeWithArgs; +using clang::tooling::FrontendActionFactory; + +/// \brief Base class for verifying some property of nodes found by a matcher. +class MatchVerifier : public MatchFinder::MatchCallback { +public: + MatchVerifier() : Verified(false), VerifyResult("Could not find match") {} + + virtual void run(const MatchFinder::MatchResult &Result) { + Verified = verify(Result); + } + + virtual bool verify(const MatchFinder::MatchResult &Result) = 0; + + void setResult(const Twine &Result) { + VerifyResult = Result.str(); + } + + testing::AssertionResult getResult() { + if (Verified) + return testing::AssertionSuccess(); + return testing::AssertionFailure() << VerifyResult; + } + +private: + bool Verified; + std::string VerifyResult; +}; + +enum Language { Lang_C, Lang_CXX }; + +/// \brief Runs a matcher over some code, and returns the result of the +/// verifier for the matched node. +template +testing::AssertionResult match(const std::string &Code, + const T &AMatcher, + MatchVerifier &Verifier, + Language L = Lang_CXX) { + MatchFinder Finder; + Finder.addMatcher(AMatcher, &Verifier); + OwningPtr Factory(newFrontendActionFactory(&Finder)); + std::vector Args; + StringRef FileName; + switch (L) { + case Lang_C: + Args.push_back("-std=c99"); + FileName = "input.c"; + break; + case Lang_CXX: + Args.push_back("-std=c++98"); + FileName = "input.cc"; + break; + } + if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName)) + return testing::AssertionFailure() << "Parsing error in \"" << Code << "\""; + return Verifier.getResult(); +} + +/// \brief Base class for verifying some property of the node with the +/// given Id that is found by a matcher. +template +class NodeVerifier : public MatchVerifier { +public: + NodeVerifier &expectId(StringRef Id) { + ExpectId = Id; + return *this; + } + + virtual bool verify(const MatchFinder::MatchResult &Result) { + const T *Node = Result.Nodes.getNodeAs(ExpectId); + if (!Node) { + this->setResult("Could not find node with id \"" + ExpectId + "\""); + return false; + } + return verifyNode(Result, *Node); + } + + virtual bool verifyNode(const MatchFinder::MatchResult &Result, + const T &Node) = 0; + +protected: + std::string ExpectId; +}; + +/// \brief Verify whether a node has the correct source location. +/// +/// By default, Node.getSourceLocation() is checked. This can be changed +/// by overriding getLocation(). +template +class LocationVerifier : public NodeVerifier { +public: + LocationVerifier &expectLocation(unsigned Line, unsigned Column) { + ExpectLine = Line; + ExpectColumn = Column; + return *this; + } + + bool verifyNode(const MatchFinder::MatchResult &Result, const T &Node) { + SourceLocation Loc = getLocation(Node); + FullSourceLoc FullLoc = Result.Context->getFullLoc(Loc); + unsigned Line = FullLoc.getSpellingLineNumber(); + unsigned Column = FullLoc.getSpellingColumnNumber(); + if (Line != ExpectLine || Column != ExpectColumn) { + this->setResult( + "Expected location <" + + Twine(ExpectLine) + ":" + Twine(ExpectColumn) + ">, found <" + + Twine(Line) + ":" + Twine(Column) + ">"); + return false; + } + return true; + } + + virtual SourceLocation getLocation(const T &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 +class RangeVerifier : public NodeVerifier { +public: + RangeVerifier &expectRange(unsigned BeginLine, unsigned BeginColumn, + unsigned EndLine, unsigned EndColumn) { + ExpectBeginLine = BeginLine; + ExpectBeginColumn = BeginColumn; + ExpectEndLine = EndLine; + ExpectEndColumn = EndColumn; + return *this; + } + + bool verifyNode(const MatchFinder::MatchResult &Result, const T &Node) { + SourceRange R = getRange(Node); + FullSourceLoc Begin = Result.Context->getFullLoc(R.getBegin()); + FullSourceLoc End = Result.Context->getFullLoc(R.getEnd()); + unsigned BeginLine = Begin.getSpellingLineNumber(); + unsigned BeginColumn = Begin.getSpellingColumnNumber(); + unsigned EndLine = End.getSpellingLineNumber(); + unsigned EndColumn = End.getSpellingColumnNumber(); + if (BeginLine != ExpectBeginLine || BeginColumn != ExpectBeginColumn || + EndLine != ExpectEndLine || EndColumn != ExpectEndColumn) { + this->setResult( + "Expected range <" + + Twine(ExpectBeginLine) + ":" + Twine(ExpectBeginColumn) + ", " + + Twine(ExpectEndLine) + ":" + Twine(ExpectEndColumn) + ">, found <" + + Twine(BeginLine) + ":" + Twine(BeginColumn) + ", " + + Twine(EndLine) + ":" + Twine(EndColumn) + ">"); + return false; + } + return true; + } + + virtual SourceRange getRange(const T &Node) { + return Node.getSourceRange(); + } + +private: + unsigned ExpectBeginLine, ExpectBeginColumn, ExpectEndLine, ExpectEndColumn; +}; + +TEST(SourceLocation, KNRParamLocation) { + LocationVerifier Verifier; + Verifier.expectLocation(1, 8).expectId("i"); + EXPECT_TRUE(match("void f(i) {}", varDecl().bind("i"), Verifier, Lang_C)); +} + +TEST(SourceLocation, KNRParamRange) { + RangeVerifier Verifier; + Verifier.expectRange(1, 8, 1, 8).expectId("i"); + EXPECT_TRUE(match("void f(i) {}", varDecl().bind("i"), Verifier, Lang_C)); +} + +} // end namespace ast_matchers +} // end namespace clang