|  | //===- unittest/Tooling/CommentHandlerTest.cpp -----------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "TestVisitor.h" | 
|  | #include "clang/Lex/Preprocessor.h" | 
|  |  | 
|  | namespace clang { | 
|  |  | 
|  | struct Comment { | 
|  | Comment(const std::string &Message, unsigned Line, unsigned Col) | 
|  | : Message(Message), Line(Line), Col(Col) { } | 
|  |  | 
|  | std::string Message; | 
|  | unsigned Line, Col; | 
|  | }; | 
|  |  | 
|  | class CommentVerifier; | 
|  | typedef std::vector<Comment> CommentList; | 
|  |  | 
|  | class CommentHandlerVisitor : public TestVisitor, public CommentHandler { | 
|  | public: | 
|  | CommentHandlerVisitor() : PP(nullptr), Verified(false) {} | 
|  |  | 
|  | ~CommentHandlerVisitor() override { | 
|  | EXPECT_TRUE(Verified) << "CommentVerifier not accessed"; | 
|  | } | 
|  |  | 
|  | bool HandleComment(Preprocessor &PP, SourceRange Loc) override { | 
|  | assert(&PP == this->PP && "Preprocessor changed!"); | 
|  |  | 
|  | SourceLocation Start = Loc.getBegin(); | 
|  | SourceManager &SM = PP.getSourceManager(); | 
|  | std::string C(SM.getCharacterData(Start), | 
|  | SM.getCharacterData(Loc.getEnd())); | 
|  |  | 
|  | bool Invalid; | 
|  | unsigned CLine = SM.getSpellingLineNumber(Start, &Invalid); | 
|  | EXPECT_TRUE(!Invalid) << "Invalid line number on comment " << C; | 
|  |  | 
|  | unsigned CCol = SM.getSpellingColumnNumber(Start, &Invalid); | 
|  | EXPECT_TRUE(!Invalid) << "Invalid column number on comment " << C; | 
|  |  | 
|  | Comments.push_back(Comment(C, CLine, CCol)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | CommentVerifier GetVerifier(); | 
|  |  | 
|  | protected: | 
|  | std::unique_ptr<ASTFrontendAction> CreateTestAction() override { | 
|  | return std::make_unique<CommentHandlerAction>(this); | 
|  | } | 
|  |  | 
|  | private: | 
|  | Preprocessor *PP; | 
|  | CommentList Comments; | 
|  | bool Verified; | 
|  |  | 
|  | class CommentHandlerAction : public TestAction { | 
|  | public: | 
|  | CommentHandlerAction(CommentHandlerVisitor *Visitor) | 
|  | : TestAction(Visitor) { } | 
|  |  | 
|  | bool BeginSourceFileAction(CompilerInstance &CI) override { | 
|  | CommentHandlerVisitor *V = | 
|  | static_cast<CommentHandlerVisitor*>(this->Visitor); | 
|  | V->PP = &CI.getPreprocessor(); | 
|  | V->PP->addCommentHandler(V); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void EndSourceFileAction() override { | 
|  | CommentHandlerVisitor *V = | 
|  | static_cast<CommentHandlerVisitor*>(this->Visitor); | 
|  | V->PP->removeCommentHandler(V); | 
|  | } | 
|  | }; | 
|  | }; | 
|  |  | 
|  | class CommentVerifier { | 
|  | CommentList::const_iterator Current; | 
|  | CommentList::const_iterator End; | 
|  | Preprocessor *PP; | 
|  |  | 
|  | public: | 
|  | CommentVerifier(const CommentList &Comments, Preprocessor *PP) | 
|  | : Current(Comments.begin()), End(Comments.end()), PP(PP) | 
|  | { } | 
|  |  | 
|  | CommentVerifier(CommentVerifier &&C) : Current(C.Current), End(C.End), PP(C.PP) { | 
|  | C.Current = C.End; | 
|  | } | 
|  |  | 
|  | ~CommentVerifier() { | 
|  | if (Current != End) { | 
|  | EXPECT_TRUE(Current == End) << "Unexpected comment \"" | 
|  | << Current->Message << "\" at line " << Current->Line << ", column " | 
|  | << Current->Col; | 
|  | } | 
|  | } | 
|  |  | 
|  | void Match(const char *Message, unsigned Line, unsigned Col) { | 
|  | EXPECT_TRUE(Current != End) << "Comment " << Message << " not found"; | 
|  | if (Current == End) return; | 
|  |  | 
|  | const Comment &C = *Current; | 
|  | EXPECT_TRUE(C.Message == Message && C.Line == Line && C.Col == Col) | 
|  | <<   "Expected comment \"" << Message | 
|  | << "\" at line " << Line   << ", column " << Col | 
|  | << "\nActual comment   \"" << C.Message | 
|  | << "\" at line " << C.Line << ", column " << C.Col; | 
|  |  | 
|  | ++Current; | 
|  | } | 
|  | }; | 
|  |  | 
|  | CommentVerifier CommentHandlerVisitor::GetVerifier() { | 
|  | Verified = true; | 
|  | return CommentVerifier(Comments, PP); | 
|  | } | 
|  |  | 
|  |  | 
|  | TEST(CommentHandlerTest, BasicTest1) { | 
|  | CommentHandlerVisitor Visitor; | 
|  | EXPECT_TRUE(Visitor.runOver("class X {}; int main() { return 0; }")); | 
|  | CommentVerifier Verifier = Visitor.GetVerifier(); | 
|  | } | 
|  |  | 
|  | TEST(CommentHandlerTest, BasicTest2) { | 
|  | CommentHandlerVisitor Visitor; | 
|  | EXPECT_TRUE(Visitor.runOver( | 
|  | "class X {}; int main() { /* comment */ return 0; }")); | 
|  | CommentVerifier Verifier = Visitor.GetVerifier(); | 
|  | Verifier.Match("/* comment */", 1, 26); | 
|  | } | 
|  |  | 
|  | TEST(CommentHandlerTest, BasicTest3) { | 
|  | CommentHandlerVisitor Visitor; | 
|  | EXPECT_TRUE(Visitor.runOver( | 
|  | "class X {}; // comment 1\n" | 
|  | "int main() {\n" | 
|  | "  // comment 2\n" | 
|  | "  return 0;\n" | 
|  | "}")); | 
|  | CommentVerifier Verifier = Visitor.GetVerifier(); | 
|  | Verifier.Match("// comment 1", 1, 13); | 
|  | Verifier.Match("// comment 2", 3, 3); | 
|  | } | 
|  |  | 
|  | TEST(CommentHandlerTest, IfBlock1) { | 
|  | CommentHandlerVisitor Visitor; | 
|  | EXPECT_TRUE(Visitor.runOver( | 
|  | "#if 0\n" | 
|  | "// ignored comment\n" | 
|  | "#endif\n" | 
|  | "// visible comment\n")); | 
|  | CommentVerifier Verifier = Visitor.GetVerifier(); | 
|  | Verifier.Match("// visible comment", 4, 1); | 
|  | } | 
|  |  | 
|  | TEST(CommentHandlerTest, IfBlock2) { | 
|  | CommentHandlerVisitor Visitor; | 
|  | EXPECT_TRUE(Visitor.runOver( | 
|  | "#define TEST        // visible_1\n" | 
|  | "#ifndef TEST        // visible_2\n" | 
|  | "                    // ignored_3\n" | 
|  | "# ifdef UNDEFINED   // ignored_4\n" | 
|  | "# endif             // ignored_5\n" | 
|  | "#elif defined(TEST) // visible_6\n" | 
|  | "# if 1              // visible_7\n" | 
|  | "                    // visible_8\n" | 
|  | "# else              // visible_9\n" | 
|  | "                    // ignored_10\n" | 
|  | "#  ifndef TEST      // ignored_11\n" | 
|  | "#  endif            // ignored_12\n" | 
|  | "# endif             // visible_13\n" | 
|  | "#endif              // visible_14\n")); | 
|  |  | 
|  | CommentVerifier Verifier = Visitor.GetVerifier(); | 
|  | Verifier.Match("// visible_1", 1, 21); | 
|  | Verifier.Match("// visible_2", 2, 21); | 
|  | Verifier.Match("// visible_6", 6, 21); | 
|  | Verifier.Match("// visible_7", 7, 21); | 
|  | Verifier.Match("// visible_8", 8, 21); | 
|  | Verifier.Match("// visible_9", 9, 21); | 
|  | Verifier.Match("// visible_13", 13, 21); | 
|  | Verifier.Match("// visible_14", 14, 21); | 
|  | } | 
|  |  | 
|  | TEST(CommentHandlerTest, IfBlock3) { | 
|  | const char *Source = | 
|  | "/* commented out ...\n" | 
|  | "#if 0\n" | 
|  | "// enclosed\n" | 
|  | "#endif */"; | 
|  |  | 
|  | CommentHandlerVisitor Visitor; | 
|  | EXPECT_TRUE(Visitor.runOver(Source)); | 
|  | CommentVerifier Verifier = Visitor.GetVerifier(); | 
|  | Verifier.Match(Source, 1, 1); | 
|  | } | 
|  |  | 
|  | TEST(CommentHandlerTest, PPDirectives) { | 
|  | CommentHandlerVisitor Visitor; | 
|  | EXPECT_TRUE(Visitor.runOver( | 
|  | "#warning Y   // ignored_1\n" // #warning takes whole line as message | 
|  | "#undef MACRO // visible_2\n" | 
|  | "#line 1      // visible_3\n")); | 
|  |  | 
|  | CommentVerifier Verifier = Visitor.GetVerifier(); | 
|  | Verifier.Match("// visible_2", 2, 14); | 
|  | Verifier.Match("// visible_3", 3, 14); | 
|  | } | 
|  |  | 
|  | } // end namespace clang |