blob: 086fdca0ef60a4856d6fdc65610741d47539bbd1 [file] [log] [blame]
Manuel Klimekd00d6f12015-08-11 11:37:48 +00001//===---- IncludeInserterTest.cpp - clang-tidy ----------------------------===//
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 "../clang-tidy/IncludeInserter.h"
11#include "clang/Lex/Preprocessor.h"
12#include "clang/Frontend/CompilerInstance.h"
13#include "ClangTidyTest.h"
14#include "gtest/gtest.h"
15
16namespace clang {
17namespace tidy {
18namespace {
19
20class IncludeInserterCheckBase : public ClangTidyCheck {
21public:
Manuel Klimek46e82c32015-08-11 12:59:22 +000022 IncludeInserterCheckBase(StringRef CheckName, ClangTidyContext *Context)
23 : ClangTidyCheck(CheckName, Context) {}
Manuel Klimekd00d6f12015-08-11 11:37:48 +000024 void registerPPCallbacks(CompilerInstance &Compiler) override {
25 Inserter.reset(new IncludeInserter(Compiler.getSourceManager(),
26 Compiler.getLangOpts(),
27 IncludeSorter::IS_Google));
28 Compiler.getPreprocessor().addPPCallbacks(Inserter->CreatePPCallbacks());
29 }
30
31 void registerMatchers(ast_matchers::MatchFinder *Finder) override {
32 Finder->addMatcher(ast_matchers::declStmt().bind("stmt"), this);
33 }
34
35 void check(const ast_matchers::MatchFinder::MatchResult &Result) override {
36 auto Fixit =
37 Inserter->CreateIncludeInsertion(Result.SourceManager->getMainFileID(),
38 HeaderToInclude(), IsAngledInclude());
39 if (Fixit) {
40 diag(Result.Nodes.getStmtAs<DeclStmt>("stmt")->getLocStart(), "foo, bar")
41 << *Fixit;
42 }
43 // Second include should yield no Fixit.
44 Fixit =
45 Inserter->CreateIncludeInsertion(Result.SourceManager->getMainFileID(),
46 HeaderToInclude(), IsAngledInclude());
47 EXPECT_FALSE(Fixit);
48 }
49
50 virtual StringRef HeaderToInclude() const = 0;
51 virtual bool IsAngledInclude() const = 0;
52
53 std::unique_ptr<IncludeInserter> Inserter;
54};
55
56class NonSystemHeaderInserterCheck : public IncludeInserterCheckBase {
57public:
Manuel Klimek46e82c32015-08-11 12:59:22 +000058 NonSystemHeaderInserterCheck(StringRef CheckName, ClangTidyContext *Context)
59 : IncludeInserterCheckBase(CheckName, Context) {}
Manuel Klimekd00d6f12015-08-11 11:37:48 +000060 StringRef HeaderToInclude() const override { return "path/to/header.h"; }
61 bool IsAngledInclude() const override { return false; }
62};
63
64class CXXSystemIncludeInserterCheck : public IncludeInserterCheckBase {
65public:
Manuel Klimek46e82c32015-08-11 12:59:22 +000066 CXXSystemIncludeInserterCheck(StringRef CheckName, ClangTidyContext *Context)
67 : IncludeInserterCheckBase(CheckName, Context) {}
Manuel Klimekd00d6f12015-08-11 11:37:48 +000068 StringRef HeaderToInclude() const override { return "set"; }
69 bool IsAngledInclude() const override { return true; }
70};
71
72template <typename Check>
73std::string runCheckOnCode(StringRef Code, StringRef Filename,
74 size_t NumWarningsExpected) {
75 std::vector<ClangTidyError> Errors;
76 return test::runCheckOnCode<Check>(Code, &Errors, Filename, None,
77 ClangTidyOptions(),
78 {// Main file include
79 {"devtools/cymbal/clang_tidy/tests/"
80 "insert_includes_test_header.h",
81 "\n"},
82 // Non system headers
83 {"path/to/a/header.h", "\n"},
84 {"path/to/z/header.h", "\n"},
85 {"path/to/header.h", "\n"},
86 // Fake system headers.
87 {"stdlib.h", "\n"},
88 {"unistd.h", "\n"},
89 {"list", "\n"},
90 {"map", "\n"},
91 {"set", "\n"},
92 {"vector", "\n"}});
93 EXPECT_EQ(NumWarningsExpected, Errors.size());
94}
95
96TEST(IncludeInserterTest, InsertAfterLastNonSystemInclude) {
97 const char *PreCode = R"(
98#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
99
100#include <list>
101#include <map>
102
103#include "path/to/a/header.h"
104
105void foo() {
106 int a = 0;
107})";
108 const char *PostCode = R"(
109#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
110
111#include <list>
112#include <map>
113
114#include "path/to/a/header.h"
115#include "path/to/header.h"
116
117void foo() {
118 int a = 0;
119})";
120
121 EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
122 PreCode, "devtools/cymbal/clang_tidy/tests/"
123 "insert_includes_test_input2.cc",
124 1));
125}
126
127TEST(IncludeInserterTest, InsertBeforeFirstNonSystemInclude) {
128 const char *PreCode = R"(
129#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
130
131#include <list>
132#include <map>
133
134#include "path/to/z/header.h"
135
136void foo() {
137 int a = 0;
138})";
139 const char *PostCode = R"(
140#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
141
142#include <list>
143#include <map>
144
145#include "path/to/header.h"
146#include "path/to/z/header.h"
147
148void foo() {
149 int a = 0;
150})";
151
152 EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
153 PreCode, "devtools/cymbal/clang_tidy/tests/"
154 "insert_includes_test_input2.cc",
155 1));
156}
157
158TEST(IncludeInserterTest, InsertBetweenNonSystemIncludes) {
159 const char *PreCode = R"(
160#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
161
162#include <list>
163#include <map>
164
165#include "path/to/a/header.h"
166#include "path/to/z/header.h"
167
168void foo() {
169 int a = 0;
170})";
171 const char *PostCode = R"(
172#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
173
174#include <list>
175#include <map>
176
177#include "path/to/a/header.h"
178#include "path/to/header.h"
179#include "path/to/z/header.h"
180
181void foo() {
182 int a = 0;
183})";
184
185 EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
186 PreCode, "devtools/cymbal/clang_tidy/tests/"
187 "insert_includes_test_input2.cc",
188 1));
189}
190
191TEST(IncludeInserterTest, NonSystemIncludeAlreadyIncluded) {
192 const char *PreCode = R"(
193#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
194
195#include <list>
196#include <map>
197
198#include "path/to/a/header.h"
199#include "path/to/header.h"
200#include "path/to/z/header.h"
201
202void foo() {
203 int a = 0;
204})";
205 EXPECT_EQ(PreCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
206 PreCode, "devtools/cymbal/clang_tidy/tests/"
207 "insert_includes_test_input2.cc",
208 0));
209}
210
211TEST(IncludeInserterTest, InsertNonSystemIncludeAfterLastCXXSystemInclude) {
212 const char *PreCode = R"(
213#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
214
215#include <list>
216#include <map>
217
218void foo() {
219 int a = 0;
220})";
221 const char *PostCode = R"(
222#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
223
224#include <list>
225#include <map>
226
227#include "path/to/header.h"
228
229void foo() {
230 int a = 0;
231})";
232
233 EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
234 PreCode, "devtools/cymbal/clang_tidy/tests/"
235 "insert_includes_test_header.cc",
236 1));
237}
238
239TEST(IncludeInserterTest, InsertNonSystemIncludeAfterMainFileInclude) {
240 const char *PreCode = R"(
241#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
242
243void foo() {
244 int a = 0;
245})";
246 const char *PostCode = R"(
247#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
248
249#include "path/to/header.h"
250
251void foo() {
252 int a = 0;
253})";
254
255 EXPECT_EQ(PostCode, runCheckOnCode<NonSystemHeaderInserterCheck>(
256 PreCode, "devtools/cymbal/clang_tidy/tests/"
257 "insert_includes_test_header.cc",
258 1));
259}
260
261TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterLastCXXSystemInclude) {
262 const char *PreCode = R"(
263#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
264
265#include <list>
266#include <map>
267
268#include "path/to/a/header.h"
269
270void foo() {
271 int a = 0;
272})";
273 const char *PostCode = R"(
274#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
275
276#include <list>
277#include <map>
278#include <set>
279
280#include "path/to/a/header.h"
281
282void foo() {
283 int a = 0;
284})";
285
286 EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
287 PreCode, "devtools/cymbal/clang_tidy/tests/"
288 "insert_includes_test_header.cc",
289 1));
290}
291
292TEST(IncludeInserterTest, InsertCXXSystemIncludeBeforeFirstCXXSystemInclude) {
293 const char *PreCode = R"(
294#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
295
296#include <vector>
297
298#include "path/to/a/header.h"
299
300void foo() {
301 int a = 0;
302})";
303 const char *PostCode = R"(
304#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
305
306#include <set>
307#include <vector>
308
309#include "path/to/a/header.h"
310
311void foo() {
312 int a = 0;
313})";
314
315 EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
316 PreCode, "devtools/cymbal/clang_tidy/tests/"
317 "insert_includes_test_header.cc",
318 1));
319}
320
321TEST(IncludeInserterTest, InsertCXXSystemIncludeBetweenCXXSystemIncludes) {
322 const char *PreCode = R"(
323#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
324
325#include <map>
326#include <vector>
327
328#include "path/to/a/header.h"
329
330void foo() {
331 int a = 0;
332})";
333 const char *PostCode = R"(
334#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
335
336#include <map>
337#include <set>
338#include <vector>
339
340#include "path/to/a/header.h"
341
342void foo() {
343 int a = 0;
344})";
345
346 EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
347 PreCode, "devtools/cymbal/clang_tidy/tests/"
348 "insert_includes_test_header.cc",
349 1));
350}
351
352TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterMainFileInclude) {
353 const char *PreCode = R"(
354#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
355
356#include "path/to/a/header.h"
357
358void foo() {
359 int a = 0;
360})";
361 const char *PostCode = R"(
362#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
363
364#include <set>
365
366#include "path/to/a/header.h"
367
368void foo() {
369 int a = 0;
370})";
371
372 EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
373 PreCode, "devtools/cymbal/clang_tidy/tests/"
374 "insert_includes_test_header.cc",
375 1));
376}
377
378TEST(IncludeInserterTest, InsertCXXSystemIncludeAfterCSystemInclude) {
379 const char *PreCode = R"(
380#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
381
382#include <stdlib.h>
383
384#include "path/to/a/header.h"
385
386void foo() {
387 int a = 0;
388})";
389 const char *PostCode = R"(
390#include "devtools/cymbal/clang_tidy/tests/insert_includes_test_header.h"
391
392#include <stdlib.h>
393
394#include <set>
395
396#include "path/to/a/header.h"
397
398void foo() {
399 int a = 0;
400})";
401
402 EXPECT_EQ(PostCode, runCheckOnCode<CXXSystemIncludeInserterCheck>(
403 PreCode, "devtools/cymbal/clang_tidy/tests/"
404 "insert_includes_test_header.cc",
405 1));
406}
407
408} // namespace
409} // namespace tidy
410} // namespace clang