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