| //===- 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<CommentHandlerVisitor>, |
| public CommentHandler { |
| typedef TestVisitor<CommentHandlerVisitor> base; |
| |
| public: |
| CommentHandlerVisitor() : base(), 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 base::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 |