//===- 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 CommentList; class CommentHandlerVisitor : public TestVisitor, public CommentHandler { typedef TestVisitor 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 CreateTestAction() override { return std::make_unique(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(this->Visitor); V->PP = &CI.getPreprocessor(); V->PP->addCommentHandler(V); return true; } void EndSourceFileAction() override { CommentHandlerVisitor *V = static_cast(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