187d8fb91SRichard Smith //===- unittest/Tooling/CommentHandlerTest.cpp -----------------------===//
287d8fb91SRichard Smith //
387d8fb91SRichard Smith //                     The LLVM Compiler Infrastructure
487d8fb91SRichard Smith //
587d8fb91SRichard Smith // This file is distributed under the University of Illinois Open Source
687d8fb91SRichard Smith // License. See LICENSE.TXT for details.
787d8fb91SRichard Smith //
887d8fb91SRichard Smith //===----------------------------------------------------------------------===//
987d8fb91SRichard Smith 
1087d8fb91SRichard Smith #include "TestVisitor.h"
1187d8fb91SRichard Smith #include "clang/Lex/Preprocessor.h"
1287d8fb91SRichard Smith 
1387d8fb91SRichard Smith namespace clang {
1487d8fb91SRichard Smith 
1587d8fb91SRichard Smith struct Comment {
1687d8fb91SRichard Smith   Comment(const std::string &Message, unsigned Line, unsigned Col)
1787d8fb91SRichard Smith     : Message(Message), Line(Line), Col(Col) { }
1887d8fb91SRichard Smith 
1987d8fb91SRichard Smith   std::string Message;
2087d8fb91SRichard Smith   unsigned Line, Col;
2187d8fb91SRichard Smith };
2287d8fb91SRichard Smith 
2387d8fb91SRichard Smith class CommentVerifier;
2487d8fb91SRichard Smith typedef std::vector<Comment> CommentList;
2587d8fb91SRichard Smith 
2687d8fb91SRichard Smith class CommentHandlerVisitor : public TestVisitor<CommentHandlerVisitor>,
2787d8fb91SRichard Smith                               public CommentHandler {
2887d8fb91SRichard Smith   typedef TestVisitor<CommentHandlerVisitor> base;
2987d8fb91SRichard Smith 
3087d8fb91SRichard Smith public:
31416fa34bSCraig Topper   CommentHandlerVisitor() : base(), PP(nullptr), Verified(false) {}
3287d8fb91SRichard Smith 
3334eb2072SAlexander Kornienko   ~CommentHandlerVisitor() override {
3487d8fb91SRichard Smith     EXPECT_TRUE(Verified) << "CommentVerifier not accessed";
3587d8fb91SRichard Smith   }
3687d8fb91SRichard Smith 
3734eb2072SAlexander Kornienko   bool HandleComment(Preprocessor &PP, SourceRange Loc) override {
3887d8fb91SRichard Smith     assert(&PP == this->PP && "Preprocessor changed!");
3987d8fb91SRichard Smith 
4087d8fb91SRichard Smith     SourceLocation Start = Loc.getBegin();
4187d8fb91SRichard Smith     SourceManager &SM = PP.getSourceManager();
4287d8fb91SRichard Smith     std::string C(SM.getCharacterData(Start),
4387d8fb91SRichard Smith                   SM.getCharacterData(Loc.getEnd()));
4487d8fb91SRichard Smith 
4587d8fb91SRichard Smith     bool Invalid;
4687d8fb91SRichard Smith     unsigned CLine = SM.getSpellingLineNumber(Start, &Invalid);
4787d8fb91SRichard Smith     EXPECT_TRUE(!Invalid) << "Invalid line number on comment " << C;
4887d8fb91SRichard Smith 
4987d8fb91SRichard Smith     unsigned CCol = SM.getSpellingColumnNumber(Start, &Invalid);
5087d8fb91SRichard Smith     EXPECT_TRUE(!Invalid) << "Invalid column number on comment " << C;
5187d8fb91SRichard Smith 
5287d8fb91SRichard Smith     Comments.push_back(Comment(C, CLine, CCol));
5387d8fb91SRichard Smith     return false;
5487d8fb91SRichard Smith   }
5587d8fb91SRichard Smith 
5687d8fb91SRichard Smith   CommentVerifier GetVerifier();
5787d8fb91SRichard Smith 
5887d8fb91SRichard Smith protected:
5934eb2072SAlexander Kornienko   ASTFrontendAction *CreateTestAction() override {
6087d8fb91SRichard Smith     return new CommentHandlerAction(this);
6187d8fb91SRichard Smith   }
6287d8fb91SRichard Smith 
6387d8fb91SRichard Smith private:
6487d8fb91SRichard Smith   Preprocessor *PP;
6587d8fb91SRichard Smith   CommentList Comments;
6687d8fb91SRichard Smith   bool Verified;
6787d8fb91SRichard Smith 
6887d8fb91SRichard Smith   class CommentHandlerAction : public base::TestAction {
6987d8fb91SRichard Smith   public:
7087d8fb91SRichard Smith     CommentHandlerAction(CommentHandlerVisitor *Visitor)
7187d8fb91SRichard Smith         : TestAction(Visitor) { }
7287d8fb91SRichard Smith 
7334eb2072SAlexander Kornienko     bool BeginSourceFileAction(CompilerInstance &CI,
7434eb2072SAlexander Kornienko                                StringRef FileName) override {
7587d8fb91SRichard Smith       CommentHandlerVisitor *V =
7687d8fb91SRichard Smith           static_cast<CommentHandlerVisitor*>(this->Visitor);
7787d8fb91SRichard Smith       V->PP = &CI.getPreprocessor();
7887d8fb91SRichard Smith       V->PP->addCommentHandler(V);
7987d8fb91SRichard Smith       return true;
8087d8fb91SRichard Smith     }
8187d8fb91SRichard Smith 
8234eb2072SAlexander Kornienko     void EndSourceFileAction() override {
8387d8fb91SRichard Smith       CommentHandlerVisitor *V =
8487d8fb91SRichard Smith           static_cast<CommentHandlerVisitor*>(this->Visitor);
8587d8fb91SRichard Smith       V->PP->removeCommentHandler(V);
8687d8fb91SRichard Smith     }
8787d8fb91SRichard Smith   };
8887d8fb91SRichard Smith };
8987d8fb91SRichard Smith 
9087d8fb91SRichard Smith class CommentVerifier {
9187d8fb91SRichard Smith   CommentList::const_iterator Current;
9287d8fb91SRichard Smith   CommentList::const_iterator End;
9387d8fb91SRichard Smith   Preprocessor *PP;
9487d8fb91SRichard Smith 
9587d8fb91SRichard Smith public:
9687d8fb91SRichard Smith   CommentVerifier(const CommentList &Comments, Preprocessor *PP)
9787d8fb91SRichard Smith       : Current(Comments.begin()), End(Comments.end()), PP(PP)
9887d8fb91SRichard Smith     { }
9987d8fb91SRichard Smith 
100*e438127bSDavid Blaikie   CommentVerifier(CommentVerifier &&C) : Current(C.Current), End(C.End), PP(C.PP) {
101*e438127bSDavid Blaikie     C.Current = C.End;
102*e438127bSDavid Blaikie   }
103*e438127bSDavid Blaikie 
10487d8fb91SRichard Smith   ~CommentVerifier() {
10587d8fb91SRichard Smith     if (Current != End) {
10687d8fb91SRichard Smith       EXPECT_TRUE(Current == End) << "Unexpected comment \""
10787d8fb91SRichard Smith         << Current->Message << "\" at line " << Current->Line << ", column "
10887d8fb91SRichard Smith         << Current->Col;
10987d8fb91SRichard Smith     }
11087d8fb91SRichard Smith   }
11187d8fb91SRichard Smith 
11287d8fb91SRichard Smith   void Match(const char *Message, unsigned Line, unsigned Col) {
11387d8fb91SRichard Smith     EXPECT_TRUE(Current != End) << "Comment " << Message << " not found";
11487d8fb91SRichard Smith     if (Current == End) return;
11587d8fb91SRichard Smith 
11687d8fb91SRichard Smith     const Comment &C = *Current;
11787d8fb91SRichard Smith     EXPECT_TRUE(C.Message == Message && C.Line == Line && C.Col == Col)
11887d8fb91SRichard Smith       <<   "Expected comment \"" << Message
11987d8fb91SRichard Smith       << "\" at line " << Line   << ", column " << Col
12087d8fb91SRichard Smith       << "\nActual comment   \"" << C.Message
12187d8fb91SRichard Smith       << "\" at line " << C.Line << ", column " << C.Col;
12287d8fb91SRichard Smith 
12387d8fb91SRichard Smith     ++Current;
12487d8fb91SRichard Smith   }
12587d8fb91SRichard Smith };
12687d8fb91SRichard Smith 
12787d8fb91SRichard Smith CommentVerifier CommentHandlerVisitor::GetVerifier() {
12887d8fb91SRichard Smith   Verified = true;
12987d8fb91SRichard Smith   return CommentVerifier(Comments, PP);
13087d8fb91SRichard Smith }
13187d8fb91SRichard Smith 
13287d8fb91SRichard Smith 
13387d8fb91SRichard Smith TEST(CommentHandlerTest, BasicTest1) {
13487d8fb91SRichard Smith   CommentHandlerVisitor Visitor;
13587d8fb91SRichard Smith   EXPECT_TRUE(Visitor.runOver("class X {}; int main() { return 0; }"));
13687d8fb91SRichard Smith   CommentVerifier Verifier = Visitor.GetVerifier();
13787d8fb91SRichard Smith }
13887d8fb91SRichard Smith 
13987d8fb91SRichard Smith TEST(CommentHandlerTest, BasicTest2) {
14087d8fb91SRichard Smith   CommentHandlerVisitor Visitor;
14187d8fb91SRichard Smith   EXPECT_TRUE(Visitor.runOver(
14287d8fb91SRichard Smith         "class X {}; int main() { /* comment */ return 0; }"));
14387d8fb91SRichard Smith   CommentVerifier Verifier = Visitor.GetVerifier();
14487d8fb91SRichard Smith   Verifier.Match("/* comment */", 1, 26);
14587d8fb91SRichard Smith }
14687d8fb91SRichard Smith 
14787d8fb91SRichard Smith TEST(CommentHandlerTest, BasicTest3) {
14887d8fb91SRichard Smith   CommentHandlerVisitor Visitor;
14987d8fb91SRichard Smith   EXPECT_TRUE(Visitor.runOver(
15087d8fb91SRichard Smith         "class X {}; // comment 1\n"
15187d8fb91SRichard Smith         "int main() {\n"
15287d8fb91SRichard Smith         "  // comment 2\n"
15387d8fb91SRichard Smith         "  return 0;\n"
15487d8fb91SRichard Smith         "}"));
15587d8fb91SRichard Smith   CommentVerifier Verifier = Visitor.GetVerifier();
15687d8fb91SRichard Smith   Verifier.Match("// comment 1", 1, 13);
15787d8fb91SRichard Smith   Verifier.Match("// comment 2", 3, 3);
15887d8fb91SRichard Smith }
15987d8fb91SRichard Smith 
16087d8fb91SRichard Smith TEST(CommentHandlerTest, IfBlock1) {
16187d8fb91SRichard Smith   CommentHandlerVisitor Visitor;
16287d8fb91SRichard Smith   EXPECT_TRUE(Visitor.runOver(
16387d8fb91SRichard Smith         "#if 0\n"
16487d8fb91SRichard Smith         "// ignored comment\n"
16587d8fb91SRichard Smith         "#endif\n"
16687d8fb91SRichard Smith         "// visible comment\n"));
16787d8fb91SRichard Smith   CommentVerifier Verifier = Visitor.GetVerifier();
16887d8fb91SRichard Smith   Verifier.Match("// visible comment", 4, 1);
16987d8fb91SRichard Smith }
17087d8fb91SRichard Smith 
17187d8fb91SRichard Smith TEST(CommentHandlerTest, IfBlock2) {
17287d8fb91SRichard Smith   CommentHandlerVisitor Visitor;
17387d8fb91SRichard Smith   EXPECT_TRUE(Visitor.runOver(
17487d8fb91SRichard Smith         "#define TEST        // visible_1\n"
17587d8fb91SRichard Smith         "#ifndef TEST        // visible_2\n"
17687d8fb91SRichard Smith         "                    // ignored_3\n"
17787d8fb91SRichard Smith         "# ifdef UNDEFINED   // ignored_4\n"
17887d8fb91SRichard Smith         "# endif             // ignored_5\n"
17987d8fb91SRichard Smith         "#elif defined(TEST) // visible_6\n"
18087d8fb91SRichard Smith         "# if 1              // visible_7\n"
18187d8fb91SRichard Smith         "                    // visible_8\n"
18287d8fb91SRichard Smith         "# else              // visible_9\n"
18387d8fb91SRichard Smith         "                    // ignored_10\n"
18487d8fb91SRichard Smith         "#  ifndef TEST      // ignored_11\n"
18587d8fb91SRichard Smith         "#  endif            // ignored_12\n"
18687d8fb91SRichard Smith         "# endif             // visible_13\n"
18787d8fb91SRichard Smith         "#endif              // visible_14\n"));
18887d8fb91SRichard Smith 
18987d8fb91SRichard Smith   CommentVerifier Verifier = Visitor.GetVerifier();
19087d8fb91SRichard Smith   Verifier.Match("// visible_1", 1, 21);
19187d8fb91SRichard Smith   Verifier.Match("// visible_2", 2, 21);
19287d8fb91SRichard Smith   Verifier.Match("// visible_6", 6, 21);
19387d8fb91SRichard Smith   Verifier.Match("// visible_7", 7, 21);
19487d8fb91SRichard Smith   Verifier.Match("// visible_8", 8, 21);
19587d8fb91SRichard Smith   Verifier.Match("// visible_9", 9, 21);
19687d8fb91SRichard Smith   Verifier.Match("// visible_13", 13, 21);
19787d8fb91SRichard Smith   Verifier.Match("// visible_14", 14, 21);
19887d8fb91SRichard Smith }
19987d8fb91SRichard Smith 
20087d8fb91SRichard Smith TEST(CommentHandlerTest, IfBlock3) {
20187d8fb91SRichard Smith   const char *Source =
20287d8fb91SRichard Smith         "/* commented out ...\n"
20387d8fb91SRichard Smith         "#if 0\n"
20487d8fb91SRichard Smith         "// enclosed\n"
20587d8fb91SRichard Smith         "#endif */";
20687d8fb91SRichard Smith 
20787d8fb91SRichard Smith   CommentHandlerVisitor Visitor;
20887d8fb91SRichard Smith   EXPECT_TRUE(Visitor.runOver(Source));
20987d8fb91SRichard Smith   CommentVerifier Verifier = Visitor.GetVerifier();
21087d8fb91SRichard Smith   Verifier.Match(Source, 1, 1);
21187d8fb91SRichard Smith }
21287d8fb91SRichard Smith 
21387d8fb91SRichard Smith TEST(CommentHandlerTest, PPDirectives) {
21487d8fb91SRichard Smith   CommentHandlerVisitor Visitor;
21587d8fb91SRichard Smith   EXPECT_TRUE(Visitor.runOver(
21687d8fb91SRichard Smith         "#warning Y   // ignored_1\n" // #warning takes whole line as message
21787d8fb91SRichard Smith         "#undef MACRO // visible_2\n"
21887d8fb91SRichard Smith         "#line 1      // visible_3\n"));
21987d8fb91SRichard Smith 
22087d8fb91SRichard Smith   CommentVerifier Verifier = Visitor.GetVerifier();
22187d8fb91SRichard Smith   Verifier.Match("// visible_2", 2, 14);
22287d8fb91SRichard Smith   Verifier.Match("// visible_3", 3, 14);
22387d8fb91SRichard Smith }
22487d8fb91SRichard Smith 
22587d8fb91SRichard Smith } // end namespace clang
226