1//===--- IncludeCleanerTest.cpp - clang-tidy -----------------------------===//
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 "ClangTidyDiagnosticConsumer.h"
10#include "ClangTidyOptions.h"
11#include "ClangTidyTest.h"
12#include "misc/IncludeCleanerCheck.h"
13#include "llvm/ADT/SmallString.h"
14#include "llvm/ADT/StringRef.h"
15#include "llvm/Support/FormatVariadic.h"
16#include "llvm/Support/Path.h"
17#include "llvm/Support/Regex.h"
18#include "llvm/Testing/Annotations/Annotations.h"
19#include "gmock/gmock.h"
20#include "gtest/gtest.h"
21#include <initializer_list>
22
23#include <optional>
24#include <vector>
25
26using namespace clang::tidy::misc;
27
28namespace clang {
29namespace tidy {
30namespace test {
31namespace {
32
33std::string
34appendPathFileSystemIndependent(std::initializer_list<std::string> Segments) {
35 llvm::SmallString<32> Result;
36 for (const auto &Segment : Segments)
37 llvm::sys::path::append(path&: Result, style: llvm::sys::path::Style::native, a: Segment);
38 return std::string(Result.str());
39}
40
41TEST(IncludeCleanerCheckTest, BasicUnusedIncludes) {
42 const char *PreCode = R"(
43#include "bar.h"
44#include <vector>
45#include "bar.h"
46)";
47 const char *PostCode = "\n";
48
49 std::vector<ClangTidyError> Errors;
50 EXPECT_EQ(PostCode,
51 runCheckOnCode<IncludeCleanerCheck>(
52 PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),
53 {{"bar.h", "#pragma once"}, {"vector", "#pragma once"}}));
54}
55
56TEST(IncludeCleanerCheckTest, SuppressUnusedIncludes) {
57 const char *PreCode = R"(
58#include "bar.h"
59#include "foo/qux.h"
60#include "baz/qux/qux.h"
61#include <vector>
62#include <list>
63)";
64
65 const char *PostCode = R"(
66#include "bar.h"
67#include "foo/qux.h"
68#include <vector>
69#include <list>
70)";
71
72 std::vector<ClangTidyError> Errors;
73 ClangTidyOptions Opts;
74 Opts.CheckOptions["test-check-0.IgnoreHeaders"] = llvm::StringRef{
75 llvm::formatv(Fmt: "bar.h;{0};{1};vector;<list>;",
76 Vals: llvm::Regex::escape(
77 String: appendPathFileSystemIndependent(Segments: {"foo", "qux.h"})),
78 Vals: llvm::Regex::escape(
79 String: appendPathFileSystemIndependent(Segments: {"baz", "qux"})))};
80 EXPECT_EQ(
81 PostCode,
82 runCheckOnCode<IncludeCleanerCheck>(
83 PreCode, &Errors, "file.cpp", {}, Opts,
84 {{"bar.h", "#pragma once"},
85 {"vector", "#pragma once"},
86 {"list", "#pragma once"},
87 {appendPathFileSystemIndependent({"foo", "qux.h"}), "#pragma once"},
88 {appendPathFileSystemIndependent({"baz", "qux", "qux.h"}),
89 "#pragma once"}}));
90}
91
92TEST(IncludeCleanerCheckTest, BasicMissingIncludes) {
93 const char *PreCode = R"(
94#include "bar.h"
95
96int BarResult = bar();
97int BazResult = baz();
98)";
99 const char *PostCode = R"(
100#include "bar.h"
101#include "baz.h"
102
103int BarResult = bar();
104int BazResult = baz();
105)";
106
107 std::vector<ClangTidyError> Errors;
108 EXPECT_EQ(PostCode, runCheckOnCode<IncludeCleanerCheck>(
109 PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),
110 {{"bar.h", R"(#pragma once
111 #include "baz.h"
112 int bar();
113 )"},
114 {"baz.h", R"(#pragma once
115 int baz();
116 )"}}));
117}
118
119TEST(IncludeCleanerCheckTest, DedupsMissingIncludes) {
120 llvm::Annotations Code(R"(
121#include "baz.h" // IWYU pragma: keep
122
123int BarResult1 = $diag1^bar();
124int BarResult2 = $diag2^bar();)");
125
126 {
127 std::vector<ClangTidyError> Errors;
128 runCheckOnCode<IncludeCleanerCheck>(Code: Code.code(), Errors: &Errors, Filename: "file.cpp", ExtraArgs: {},
129 ExtraOptions: ClangTidyOptions(),
130 PathsToContent: {{"baz.h", R"(#pragma once
131 #include "bar.h"
132 )"},
133 {"bar.h", R"(#pragma once
134 int bar();
135 )"}});
136 ASSERT_THAT(Errors.size(), testing::Eq(1U));
137 EXPECT_EQ(Errors.front().Message.Message,
138 "no header providing \"bar\" is directly included");
139 EXPECT_EQ(Errors.front().Message.FileOffset, Code.point("diag1"));
140 }
141 {
142 std::vector<ClangTidyError> Errors;
143 ClangTidyOptions Opts;
144 Opts.CheckOptions["test-check-0.DeduplicateFindings"] = "false";
145 runCheckOnCode<IncludeCleanerCheck>(Code: Code.code(), Errors: &Errors, Filename: "file.cpp", ExtraArgs: {},
146 ExtraOptions: Opts,
147 PathsToContent: {{"baz.h", R"(#pragma once
148 #include "bar.h"
149 )"},
150 {"bar.h", R"(#pragma once
151 int bar();
152 )"}});
153 ASSERT_THAT(Errors.size(), testing::Eq(2U));
154 EXPECT_EQ(Errors.front().Message.Message,
155 "no header providing \"bar\" is directly included");
156 EXPECT_EQ(Errors.front().Message.FileOffset, Code.point("diag1"));
157 EXPECT_EQ(Errors.back().Message.Message,
158 "no header providing \"bar\" is directly included");
159 EXPECT_EQ(Errors.back().Message.FileOffset, Code.point("diag2"));
160 }
161}
162
163TEST(IncludeCleanerCheckTest, SuppressMissingIncludes) {
164 const char *PreCode = R"(
165#include "bar.h"
166
167int BarResult = bar();
168int BazResult = baz();
169int QuxResult = qux();
170int PrivResult = test();
171std::vector x;
172)";
173
174 ClangTidyOptions Opts;
175 Opts.CheckOptions["test-check-0.IgnoreHeaders"] = llvm::StringRef{
176 "public.h;<vector>;baz.h;" +
177 llvm::Regex::escape(String: appendPathFileSystemIndependent(Segments: {"foo", "qux.h"}))};
178 std::vector<ClangTidyError> Errors;
179 EXPECT_EQ(PreCode, runCheckOnCode<IncludeCleanerCheck>(
180 PreCode, &Errors, "file.cpp", {}, Opts,
181 {{"bar.h", R"(#pragma once
182 #include "baz.h"
183 #include "foo/qux.h"
184 #include "private.h"
185 int bar();
186 namespace std { struct vector {}; }
187 )"},
188 {"baz.h", R"(#pragma once
189 int baz();
190 )"},
191 {"private.h", R"(#pragma once
192 // IWYU pragma: private, include "public.h"
193 int test();
194 )"},
195 {appendPathFileSystemIndependent({"foo", "qux.h"}),
196 R"(#pragma once
197 int qux();
198 )"}}));
199}
200
201TEST(IncludeCleanerCheckTest, MultipleTimeMissingInclude) {
202 const char *PreCode = R"(
203#include "bar.h"
204
205int BarResult = bar();
206int BazResult_0 = baz_0();
207int BazResult_1 = baz_1();
208)";
209 const char *PostCode = R"(
210#include "bar.h"
211#include "baz.h"
212
213int BarResult = bar();
214int BazResult_0 = baz_0();
215int BazResult_1 = baz_1();
216)";
217
218 std::vector<ClangTidyError> Errors;
219 EXPECT_EQ(PostCode, runCheckOnCode<IncludeCleanerCheck>(
220 PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),
221 {{"bar.h", R"(#pragma once
222 #include "baz.h"
223 int bar();
224 )"},
225 {"baz.h", R"(#pragma once
226 int baz_0();
227 int baz_1();
228 )"}}));
229}
230
231TEST(IncludeCleanerCheckTest, SystemMissingIncludes) {
232 const char *PreCode = R"(
233#include <vector>
234
235std::string HelloString;
236std::vector Vec;
237)";
238 const char *PostCode = R"(
239#include <string>
240#include <vector>
241
242std::string HelloString;
243std::vector Vec;
244)";
245
246 std::vector<ClangTidyError> Errors;
247 EXPECT_EQ(PostCode, runCheckOnCode<IncludeCleanerCheck>(
248 PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),
249 {{"string", R"(#pragma once
250 namespace std { class string {}; }
251 )"},
252 {"vector", R"(#pragma once
253 #include <string>
254 namespace std { class vector {}; }
255 )"}}));
256}
257
258TEST(IncludeCleanerCheckTest, PragmaMissingIncludes) {
259 const char *PreCode = R"(
260#include "bar.h"
261
262int BarResult = bar();
263int FooBarResult = foobar();
264)";
265 const char *PostCode = R"(
266#include "bar.h"
267#include "public.h"
268
269int BarResult = bar();
270int FooBarResult = foobar();
271)";
272
273 std::vector<ClangTidyError> Errors;
274 EXPECT_EQ(PostCode, runCheckOnCode<IncludeCleanerCheck>(
275 PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),
276 {{"bar.h", R"(#pragma once
277 #include "private.h"
278 int bar();
279 )"},
280 {"private.h", R"(#pragma once
281 // IWYU pragma: private, include "public.h"
282 int foobar();
283 )"}}));
284}
285
286TEST(IncludeCleanerCheckTest, DeclFromMacroExpansion) {
287 const char *PreCode = R"(
288#include "foo.h"
289
290DECLARE(myfunc) {
291 int a;
292}
293)";
294
295 std::vector<ClangTidyError> Errors;
296 EXPECT_EQ(PreCode, runCheckOnCode<IncludeCleanerCheck>(
297 PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),
298 {{"foo.h",
299 R"(#pragma once
300 #define DECLARE(X) void X()
301 )"}}));
302
303 PreCode = R"(
304#include "foo.h"
305
306DECLARE {
307 int a;
308}
309)";
310
311 EXPECT_EQ(PreCode, runCheckOnCode<IncludeCleanerCheck>(
312 PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),
313 {{"foo.h",
314 R"(#pragma once
315 #define DECLARE void myfunc()
316 )"}}));
317}
318
319TEST(IncludeCleanerCheckTest, UnusedIncludes) {
320 const char *PreCode = R"(
321#include "bar.h")";
322
323 {
324 std::vector<ClangTidyError> Errors;
325 runCheckOnCode<IncludeCleanerCheck>(Code: PreCode, Errors: &Errors, Filename: "file.cpp", ExtraArgs: {},
326 ExtraOptions: ClangTidyOptions(),
327 PathsToContent: {{"bar.h", "#pragma once"}});
328 ASSERT_THAT(Errors.size(), testing::Eq(1U));
329 EXPECT_EQ(Errors.front().Message.Message,
330 "included header bar.h is not used directly");
331 }
332 {
333 std::vector<ClangTidyError> Errors;
334 ClangTidyOptions Opts;
335 Opts.CheckOptions["test-check-0.UnusedIncludes"] = "false";
336 runCheckOnCode<IncludeCleanerCheck>(Code: PreCode, Errors: &Errors, Filename: "file.cpp", ExtraArgs: {}, ExtraOptions: Opts,
337 PathsToContent: {{"bar.h", "#pragma once"}});
338 ASSERT_THAT(Errors.size(), testing::Eq(0U));
339 }
340}
341
342TEST(IncludeCleanerCheckTest, MissingIncludes) {
343 const char *PreCode = R"(
344#include "baz.h" // IWYU pragma: keep
345
346int BarResult1 = bar();)";
347
348 {
349 std::vector<ClangTidyError> Errors;
350 runCheckOnCode<IncludeCleanerCheck>(Code: PreCode, Errors: &Errors, Filename: "file.cpp", ExtraArgs: {},
351 ExtraOptions: ClangTidyOptions(),
352 PathsToContent: {{"baz.h", R"(#pragma once
353 #include "bar.h"
354 )"},
355 {"bar.h", R"(#pragma once
356 int bar();
357 )"}});
358 ASSERT_THAT(Errors.size(), testing::Eq(1U));
359 EXPECT_EQ(Errors.front().Message.Message,
360 "no header providing \"bar\" is directly included");
361 }
362 {
363 std::vector<ClangTidyError> Errors;
364 ClangTidyOptions Opts;
365 Opts.CheckOptions["test-check-0.MissingIncludes"] = "false";
366 runCheckOnCode<IncludeCleanerCheck>(Code: PreCode, Errors: &Errors, Filename: "file.cpp", ExtraArgs: {}, ExtraOptions: Opts,
367 PathsToContent: {{"baz.h", R"(#pragma once
368 #include "bar.h"
369 )"},
370 {"bar.h", R"(#pragma once
371 int bar();
372 )"}});
373 ASSERT_THAT(Errors.size(), testing::Eq(0U));
374 }
375}
376
377} // namespace
378} // namespace test
379} // namespace tidy
380} // namespace clang
381

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of clang-tools-extra/unittests/clang-tidy/IncludeCleanerTest.cpp