Manuel Klimek | 975a949 | 2012-11-06 17:31:40 +0000 | [diff] [blame] | 1 | //===- unittest/AST/SourceLocationTest.cpp - AST source loc unit tests ----===// |
| 2 | // |
| 3 | // The LLVM Compiler Infrastructure |
| 4 | // |
| 5 | // This file is distributed under the University of Illinois Open Source |
| 6 | // License. See LICENSE.TXT for details. |
| 7 | // |
| 8 | //===----------------------------------------------------------------------===// |
| 9 | // |
| 10 | // This file contains tests for SourceLocation and SourceRange fields |
| 11 | // in AST nodes. |
| 12 | // |
| 13 | // FIXME: In the long-term, when we test more than source locations, we may |
| 14 | // want to have a unit test file for an AST node (or group of related nodes), |
| 15 | // rather than a unit test file for source locations for all AST nodes. |
| 16 | // |
| 17 | //===----------------------------------------------------------------------===// |
| 18 | |
| 19 | #include "clang/AST/ASTContext.h" |
| 20 | #include "clang/ASTMatchers/ASTMatchers.h" |
| 21 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
| 22 | #include "clang/Tooling/Tooling.h" |
| 23 | #include "gtest/gtest.h" |
| 24 | |
| 25 | namespace clang { |
| 26 | namespace ast_matchers { |
| 27 | |
| 28 | using clang::tooling::newFrontendActionFactory; |
| 29 | using clang::tooling::runToolOnCodeWithArgs; |
| 30 | using clang::tooling::FrontendActionFactory; |
| 31 | |
| 32 | enum Language { Lang_C, Lang_CXX }; |
| 33 | |
| 34 | /// \brief Base class for verifying some property of nodes found by a matcher. |
| 35 | /// |
| 36 | /// FIXME: This class should be shared with other AST tests. |
| 37 | template <typename NodeType> |
| 38 | class MatchVerifier : public MatchFinder::MatchCallback { |
| 39 | public: |
| 40 | template <typename MatcherType> |
| 41 | testing::AssertionResult match(const std::string &Code, |
| 42 | const MatcherType &AMatcher) { |
| 43 | return match(Code, AMatcher, Lang_CXX); |
| 44 | } |
| 45 | |
| 46 | template <typename MatcherType> |
| 47 | testing::AssertionResult match(const std::string &Code, |
| 48 | const MatcherType &AMatcher, Language L); |
| 49 | |
| 50 | protected: |
| 51 | virtual void run(const MatchFinder::MatchResult &Result); |
| 52 | virtual void verify(const MatchFinder::MatchResult &Result, |
| 53 | const NodeType &Node) = 0; |
| 54 | |
| 55 | void setFailure(const Twine &Result) { |
| 56 | Verified = false; |
| 57 | VerifyResult = Result.str(); |
| 58 | } |
| 59 | |
| 60 | private: |
| 61 | bool Verified; |
| 62 | std::string VerifyResult; |
| 63 | }; |
| 64 | |
| 65 | /// \brief Runs a matcher over some code, and returns the result of the |
| 66 | /// verifier for the matched node. |
| 67 | template <typename NodeType> template <typename MatcherType> |
| 68 | testing::AssertionResult MatchVerifier<NodeType>::match( |
| 69 | const std::string &Code, const MatcherType &AMatcher, Language L) { |
| 70 | MatchFinder Finder; |
| 71 | Finder.addMatcher(AMatcher.bind(""), this); |
| 72 | OwningPtr<FrontendActionFactory> Factory(newFrontendActionFactory(&Finder)); |
| 73 | |
| 74 | std::vector<std::string> Args; |
| 75 | StringRef FileName; |
| 76 | switch (L) { |
| 77 | case Lang_C: |
| 78 | Args.push_back("-std=c99"); |
| 79 | FileName = "input.c"; |
| 80 | break; |
| 81 | case Lang_CXX: |
| 82 | Args.push_back("-std=c++98"); |
| 83 | FileName = "input.cc"; |
| 84 | break; |
| 85 | } |
| 86 | |
| 87 | // Default to failure in case callback is never called |
| 88 | setFailure("Could not find match"); |
| 89 | if (!runToolOnCodeWithArgs(Factory->create(), Code, Args, FileName)) |
| 90 | return testing::AssertionFailure() << "Parsing error"; |
| 91 | if (!Verified) |
| 92 | return testing::AssertionFailure() << VerifyResult; |
| 93 | return testing::AssertionSuccess(); |
| 94 | } |
| 95 | |
| 96 | template <typename NodeType> |
| 97 | void MatchVerifier<NodeType>::run(const MatchFinder::MatchResult &Result) { |
| 98 | const NodeType *Node = Result.Nodes.getNodeAs<NodeType>(""); |
| 99 | if (!Node) { |
| 100 | setFailure("Matched node has wrong type"); |
| 101 | } else { |
| 102 | // Callback has been called, default to success |
| 103 | Verified = true; |
| 104 | verify(Result, *Node); |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | /// \brief Verify whether a node has the correct source location. |
| 109 | /// |
| 110 | /// By default, Node.getSourceLocation() is checked. This can be changed |
| 111 | /// by overriding getLocation(). |
| 112 | template <typename NodeType> |
| 113 | class LocationVerifier : public MatchVerifier<NodeType> { |
| 114 | public: |
| 115 | void expectLocation(unsigned Line, unsigned Column) { |
| 116 | ExpectLine = Line; |
| 117 | ExpectColumn = Column; |
| 118 | } |
| 119 | |
| 120 | protected: |
| 121 | void verify(const MatchFinder::MatchResult &Result, const NodeType &Node) { |
| 122 | SourceLocation Loc = getLocation(Node); |
| 123 | unsigned Line = Result.SourceManager->getSpellingLineNumber(Loc); |
| 124 | unsigned Column = Result.SourceManager->getSpellingColumnNumber(Loc); |
| 125 | if (Line != ExpectLine || Column != ExpectColumn) { |
| 126 | std::string MsgStr; |
| 127 | llvm::raw_string_ostream Msg(MsgStr); |
| 128 | Msg << "Expected location <" << ExpectLine << ":" << ExpectColumn |
| 129 | << ">, found <"; |
| 130 | Loc.print(Msg, *Result.SourceManager); |
| 131 | Msg << '>'; |
| 132 | this->setFailure(Msg.str()); |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | virtual SourceLocation getLocation(const NodeType &Node) { |
| 137 | return Node.getLocation(); |
| 138 | } |
| 139 | |
| 140 | private: |
| 141 | unsigned ExpectLine, ExpectColumn; |
| 142 | }; |
| 143 | |
| 144 | /// \brief Verify whether a node has the correct source range. |
| 145 | /// |
| 146 | /// By default, Node.getSourceRange() is checked. This can be changed |
| 147 | /// by overriding getRange(). |
| 148 | template <typename NodeType> |
| 149 | class RangeVerifier : public MatchVerifier<NodeType> { |
| 150 | public: |
| 151 | void expectRange(unsigned BeginLine, unsigned BeginColumn, |
| 152 | unsigned EndLine, unsigned EndColumn) { |
| 153 | ExpectBeginLine = BeginLine; |
| 154 | ExpectBeginColumn = BeginColumn; |
| 155 | ExpectEndLine = EndLine; |
| 156 | ExpectEndColumn = EndColumn; |
| 157 | } |
| 158 | |
| 159 | protected: |
| 160 | void verify(const MatchFinder::MatchResult &Result, const NodeType &Node) { |
| 161 | SourceRange R = getRange(Node); |
| 162 | SourceLocation Begin = R.getBegin(); |
| 163 | SourceLocation End = R.getEnd(); |
| 164 | unsigned BeginLine = Result.SourceManager->getSpellingLineNumber(Begin); |
| 165 | unsigned BeginColumn = Result.SourceManager->getSpellingColumnNumber(Begin); |
| 166 | unsigned EndLine = Result.SourceManager->getSpellingLineNumber(End); |
| 167 | unsigned EndColumn = Result.SourceManager->getSpellingColumnNumber(End); |
| 168 | if (BeginLine != ExpectBeginLine || BeginColumn != ExpectBeginColumn || |
| 169 | EndLine != ExpectEndLine || EndColumn != ExpectEndColumn) { |
| 170 | std::string MsgStr; |
| 171 | llvm::raw_string_ostream Msg(MsgStr); |
| 172 | Msg << "Expected range <" << ExpectBeginLine << ":" << ExpectBeginColumn |
| 173 | << '-' << ExpectEndLine << ":" << ExpectEndColumn << ">, found <"; |
| 174 | Begin.print(Msg, *Result.SourceManager); |
| 175 | Msg << '-'; |
| 176 | End.print(Msg, *Result.SourceManager); |
| 177 | Msg << '>'; |
| 178 | this->setFailure(Msg.str()); |
| 179 | } |
| 180 | } |
| 181 | |
| 182 | virtual SourceRange getRange(const NodeType &Node) { |
| 183 | return Node.getSourceRange(); |
| 184 | } |
| 185 | |
| 186 | private: |
| 187 | unsigned ExpectBeginLine, ExpectBeginColumn, ExpectEndLine, ExpectEndColumn; |
| 188 | }; |
| 189 | |
| 190 | TEST(MatchVerifier, ParseError) { |
| 191 | LocationVerifier<VarDecl> Verifier; |
| 192 | Verifier.expectLocation(1, 1); |
| 193 | EXPECT_FALSE(Verifier.match("int i", varDecl())); |
| 194 | } |
| 195 | |
| 196 | TEST(MatchVerifier, NoMatch) { |
| 197 | LocationVerifier<VarDecl> Verifier; |
| 198 | Verifier.expectLocation(1, 1); |
| 199 | EXPECT_FALSE(Verifier.match("int i;", recordDecl())); |
| 200 | } |
| 201 | |
| 202 | TEST(MatchVerifier, WrongType) { |
| 203 | LocationVerifier<RecordDecl> Verifier; |
| 204 | Verifier.expectLocation(1, 1); |
| 205 | EXPECT_FALSE(Verifier.match("int i;", varDecl())); |
| 206 | } |
| 207 | |
| 208 | TEST(LocationVerifier, WrongLocation) { |
| 209 | LocationVerifier<VarDecl> Verifier; |
| 210 | Verifier.expectLocation(1, 1); |
| 211 | EXPECT_FALSE(Verifier.match("int i;", varDecl())); |
| 212 | } |
| 213 | |
| 214 | TEST(RangeVerifier, WrongRange) { |
| 215 | RangeVerifier<VarDecl> Verifier; |
| 216 | Verifier.expectRange(1, 1, 1, 1); |
| 217 | EXPECT_FALSE(Verifier.match("int i;", varDecl())); |
| 218 | } |
| 219 | |
| 220 | class LabelDeclRangeVerifier : public RangeVerifier<LabelStmt> { |
| 221 | protected: |
| 222 | virtual SourceRange getRange(const LabelStmt &Node) { |
| 223 | return Node.getDecl()->getSourceRange(); |
| 224 | } |
| 225 | }; |
| 226 | |
| 227 | TEST(LabelDecl, Range) { |
| 228 | LabelDeclRangeVerifier Verifier; |
| 229 | Verifier.expectRange(1, 12, 1, 12); |
| 230 | EXPECT_TRUE(Verifier.match("void f() { l: return; }", labelStmt())); |
| 231 | } |
| 232 | |
| 233 | TEST(LabelStmt, Range) { |
| 234 | RangeVerifier<LabelStmt> Verifier; |
| 235 | Verifier.expectRange(1, 12, 1, 15); |
| 236 | EXPECT_TRUE(Verifier.match("void f() { l: return; }", labelStmt())); |
| 237 | } |
| 238 | |
| 239 | TEST(ParmVarDecl, KNRLocation) { |
| 240 | LocationVerifier<ParmVarDecl> Verifier; |
| 241 | Verifier.expectLocation(1, 8); |
| 242 | EXPECT_TRUE(Verifier.match("void f(i) {}", varDecl(), Lang_C)); |
| 243 | } |
| 244 | |
| 245 | TEST(ParmVarDecl, KNRRange) { |
| 246 | RangeVerifier<ParmVarDecl> Verifier; |
| 247 | Verifier.expectRange(1, 8, 1, 8); |
| 248 | EXPECT_TRUE(Verifier.match("void f(i) {}", varDecl(), Lang_C)); |
| 249 | } |
| 250 | |
David Blaikie | 5a78985 | 2012-11-07 17:17:07 +0000 | [diff] [blame] | 251 | TEST(CXXNewExpr, ArrayRange) { |
| 252 | RangeVerifier<CXXNewExpr> Verifier; |
| 253 | Verifier.expectRange(1, 12, 1, 22); |
| 254 | EXPECT_TRUE(Verifier.match("void f() { new int[10]; }", newExpr())); |
| 255 | } |
| 256 | |
Abramo Bagnara | 13fd684 | 2012-11-08 13:52:58 +0000 | [diff] [blame^] | 257 | TEST(MemberExpr, ImplicitMemberRange) { |
| 258 | RangeVerifier<MemberExpr> Verifier; |
| 259 | Verifier.expectRange(2, 30, 2, 30); |
| 260 | EXPECT_TRUE(Verifier.match("struct S { operator int() const; };\n" |
| 261 | "int foo(const S& s) { return s; }", |
| 262 | memberExpr())); |
| 263 | } |
| 264 | |
Manuel Klimek | 975a949 | 2012-11-06 17:31:40 +0000 | [diff] [blame] | 265 | } // end namespace ast_matchers |
| 266 | } // end namespace clang |