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