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