1 //===- unittest/Tooling/CommentHandlerTest.cpp -----------------------===//
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 #include "TestVisitor.h"
11 #include "clang/Lex/Preprocessor.h"
12 
13 namespace clang {
14 
15 struct Comment {
Commentclang::Comment16   Comment(const std::string &Message, unsigned Line, unsigned Col)
17     : Message(Message), Line(Line), Col(Col) { }
18 
19   std::string Message;
20   unsigned Line, Col;
21 };
22 
23 class CommentVerifier;
24 typedef std::vector<Comment> CommentList;
25 
26 class CommentHandlerVisitor : public TestVisitor<CommentHandlerVisitor>,
27                               public CommentHandler {
28   typedef TestVisitor<CommentHandlerVisitor> base;
29 
30 public:
CommentHandlerVisitor()31   CommentHandlerVisitor() : base(), PP(nullptr), Verified(false) {}
32 
~CommentHandlerVisitor()33   ~CommentHandlerVisitor() override {
34     EXPECT_TRUE(Verified) << "CommentVerifier not accessed";
35   }
36 
HandleComment(Preprocessor & PP,SourceRange Loc)37   bool HandleComment(Preprocessor &PP, SourceRange Loc) override {
38     assert(&PP == this->PP && "Preprocessor changed!");
39 
40     SourceLocation Start = Loc.getBegin();
41     SourceManager &SM = PP.getSourceManager();
42     std::string C(SM.getCharacterData(Start),
43                   SM.getCharacterData(Loc.getEnd()));
44 
45     bool Invalid;
46     unsigned CLine = SM.getSpellingLineNumber(Start, &Invalid);
47     EXPECT_TRUE(!Invalid) << "Invalid line number on comment " << C;
48 
49     unsigned CCol = SM.getSpellingColumnNumber(Start, &Invalid);
50     EXPECT_TRUE(!Invalid) << "Invalid column number on comment " << C;
51 
52     Comments.push_back(Comment(C, CLine, CCol));
53     return false;
54   }
55 
56   CommentVerifier GetVerifier();
57 
58 protected:
CreateTestAction()59   ASTFrontendAction *CreateTestAction() override {
60     return new CommentHandlerAction(this);
61   }
62 
63 private:
64   Preprocessor *PP;
65   CommentList Comments;
66   bool Verified;
67 
68   class CommentHandlerAction : public base::TestAction {
69   public:
CommentHandlerAction(CommentHandlerVisitor * Visitor)70     CommentHandlerAction(CommentHandlerVisitor *Visitor)
71         : TestAction(Visitor) { }
72 
BeginSourceFileAction(CompilerInstance & CI,StringRef FileName)73     bool BeginSourceFileAction(CompilerInstance &CI,
74                                StringRef FileName) override {
75       CommentHandlerVisitor *V =
76           static_cast<CommentHandlerVisitor*>(this->Visitor);
77       V->PP = &CI.getPreprocessor();
78       V->PP->addCommentHandler(V);
79       return true;
80     }
81 
EndSourceFileAction()82     void EndSourceFileAction() override {
83       CommentHandlerVisitor *V =
84           static_cast<CommentHandlerVisitor*>(this->Visitor);
85       V->PP->removeCommentHandler(V);
86     }
87   };
88 };
89 
90 class CommentVerifier {
91   CommentList::const_iterator Current;
92   CommentList::const_iterator End;
93   Preprocessor *PP;
94 
95 public:
CommentVerifier(const CommentList & Comments,Preprocessor * PP)96   CommentVerifier(const CommentList &Comments, Preprocessor *PP)
97       : Current(Comments.begin()), End(Comments.end()), PP(PP)
98     { }
99 
CommentVerifier(CommentVerifier && C)100   CommentVerifier(CommentVerifier &&C) : Current(C.Current), End(C.End), PP(C.PP) {
101     C.Current = C.End;
102   }
103 
~CommentVerifier()104   ~CommentVerifier() {
105     if (Current != End) {
106       EXPECT_TRUE(Current == End) << "Unexpected comment \""
107         << Current->Message << "\" at line " << Current->Line << ", column "
108         << Current->Col;
109     }
110   }
111 
Match(const char * Message,unsigned Line,unsigned Col)112   void Match(const char *Message, unsigned Line, unsigned Col) {
113     EXPECT_TRUE(Current != End) << "Comment " << Message << " not found";
114     if (Current == End) return;
115 
116     const Comment &C = *Current;
117     EXPECT_TRUE(C.Message == Message && C.Line == Line && C.Col == Col)
118       <<   "Expected comment \"" << Message
119       << "\" at line " << Line   << ", column " << Col
120       << "\nActual comment   \"" << C.Message
121       << "\" at line " << C.Line << ", column " << C.Col;
122 
123     ++Current;
124   }
125 };
126 
GetVerifier()127 CommentVerifier CommentHandlerVisitor::GetVerifier() {
128   Verified = true;
129   return CommentVerifier(Comments, PP);
130 }
131 
132 
TEST(CommentHandlerTest,BasicTest1)133 TEST(CommentHandlerTest, BasicTest1) {
134   CommentHandlerVisitor Visitor;
135   EXPECT_TRUE(Visitor.runOver("class X {}; int main() { return 0; }"));
136   CommentVerifier Verifier = Visitor.GetVerifier();
137 }
138 
TEST(CommentHandlerTest,BasicTest2)139 TEST(CommentHandlerTest, BasicTest2) {
140   CommentHandlerVisitor Visitor;
141   EXPECT_TRUE(Visitor.runOver(
142         "class X {}; int main() { /* comment */ return 0; }"));
143   CommentVerifier Verifier = Visitor.GetVerifier();
144   Verifier.Match("/* comment */", 1, 26);
145 }
146 
TEST(CommentHandlerTest,BasicTest3)147 TEST(CommentHandlerTest, BasicTest3) {
148   CommentHandlerVisitor Visitor;
149   EXPECT_TRUE(Visitor.runOver(
150         "class X {}; // comment 1\n"
151         "int main() {\n"
152         "  // comment 2\n"
153         "  return 0;\n"
154         "}"));
155   CommentVerifier Verifier = Visitor.GetVerifier();
156   Verifier.Match("// comment 1", 1, 13);
157   Verifier.Match("// comment 2", 3, 3);
158 }
159 
TEST(CommentHandlerTest,IfBlock1)160 TEST(CommentHandlerTest, IfBlock1) {
161   CommentHandlerVisitor Visitor;
162   EXPECT_TRUE(Visitor.runOver(
163         "#if 0\n"
164         "// ignored comment\n"
165         "#endif\n"
166         "// visible comment\n"));
167   CommentVerifier Verifier = Visitor.GetVerifier();
168   Verifier.Match("// visible comment", 4, 1);
169 }
170 
TEST(CommentHandlerTest,IfBlock2)171 TEST(CommentHandlerTest, IfBlock2) {
172   CommentHandlerVisitor Visitor;
173   EXPECT_TRUE(Visitor.runOver(
174         "#define TEST        // visible_1\n"
175         "#ifndef TEST        // visible_2\n"
176         "                    // ignored_3\n"
177         "# ifdef UNDEFINED   // ignored_4\n"
178         "# endif             // ignored_5\n"
179         "#elif defined(TEST) // visible_6\n"
180         "# if 1              // visible_7\n"
181         "                    // visible_8\n"
182         "# else              // visible_9\n"
183         "                    // ignored_10\n"
184         "#  ifndef TEST      // ignored_11\n"
185         "#  endif            // ignored_12\n"
186         "# endif             // visible_13\n"
187         "#endif              // visible_14\n"));
188 
189   CommentVerifier Verifier = Visitor.GetVerifier();
190   Verifier.Match("// visible_1", 1, 21);
191   Verifier.Match("// visible_2", 2, 21);
192   Verifier.Match("// visible_6", 6, 21);
193   Verifier.Match("// visible_7", 7, 21);
194   Verifier.Match("// visible_8", 8, 21);
195   Verifier.Match("// visible_9", 9, 21);
196   Verifier.Match("// visible_13", 13, 21);
197   Verifier.Match("// visible_14", 14, 21);
198 }
199 
TEST(CommentHandlerTest,IfBlock3)200 TEST(CommentHandlerTest, IfBlock3) {
201   const char *Source =
202         "/* commented out ...\n"
203         "#if 0\n"
204         "// enclosed\n"
205         "#endif */";
206 
207   CommentHandlerVisitor Visitor;
208   EXPECT_TRUE(Visitor.runOver(Source));
209   CommentVerifier Verifier = Visitor.GetVerifier();
210   Verifier.Match(Source, 1, 1);
211 }
212 
TEST(CommentHandlerTest,PPDirectives)213 TEST(CommentHandlerTest, PPDirectives) {
214   CommentHandlerVisitor Visitor;
215   EXPECT_TRUE(Visitor.runOver(
216         "#warning Y   // ignored_1\n" // #warning takes whole line as message
217         "#undef MACRO // visible_2\n"
218         "#line 1      // visible_3\n"));
219 
220   CommentVerifier Verifier = Visitor.GetVerifier();
221   Verifier.Match("// visible_2", 2, 14);
222   Verifier.Match("// visible_3", 3, 14);
223 }
224 
225 } // end namespace clang
226