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