Bump version to 19.1.0 (final)
[llvm-project.git] / clang-tools-extra / unittests / clang-tidy / IncludeCleanerTest.cpp
blob8da1051a860a8c7b20b7043aae6f1db1228c0e8c
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 //===----------------------------------------------------------------------===//
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>
23 #include <optional>
24 #include <vector>
26 using namespace clang::tidy::misc;
28 namespace clang {
29 namespace tidy {
30 namespace test {
31 namespace {
33 std::string
34 appendPathFileSystemIndependent(std::initializer_list<std::string> Segments) {
35 llvm::SmallString<32> Result;
36 for (const auto &Segment : Segments)
37 llvm::sys::path::append(Result, llvm::sys::path::Style::native, Segment);
38 return std::string(Result.str());
41 TEST(IncludeCleanerCheckTest, BasicUnusedIncludes) {
42 const char *PreCode = R"(
43 #include "bar.h"
44 #include <vector>
45 #include "bar.h"
46 )";
47 const char *PostCode = "\n";
49 std::vector<ClangTidyError> Errors;
50 EXPECT_EQ(PostCode,
51 runCheckOnCode<IncludeCleanerCheck>(
52 PreCode, &Errors, "file.cpp", std::nullopt, ClangTidyOptions(),
53 {{"bar.h", "#pragma once"}, {"vector", "#pragma once"}}));
56 TEST(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 )";
65 const char *PostCode = R"(
66 #include "bar.h"
67 #include "foo/qux.h"
68 #include <vector>
69 #include <list>
70 )";
72 std::vector<ClangTidyError> Errors;
73 ClangTidyOptions Opts;
74 Opts.CheckOptions["IgnoreHeaders"] = llvm::StringRef{llvm::formatv(
75 "bar.h;{0};{1};vector;<list>;",
76 llvm::Regex::escape(appendPathFileSystemIndependent({"foo", "qux.h"})),
77 llvm::Regex::escape(appendPathFileSystemIndependent({"baz", "qux"})))};
78 EXPECT_EQ(
79 PostCode,
80 runCheckOnCode<IncludeCleanerCheck>(
81 PreCode, &Errors, "file.cpp", std::nullopt, Opts,
82 {{"bar.h", "#pragma once"},
83 {"vector", "#pragma once"},
84 {"list", "#pragma once"},
85 {appendPathFileSystemIndependent({"foo", "qux.h"}), "#pragma once"},
86 {appendPathFileSystemIndependent({"baz", "qux", "qux.h"}),
87 "#pragma once"}}));
90 TEST(IncludeCleanerCheckTest, BasicMissingIncludes) {
91 const char *PreCode = R"(
92 #include "bar.h"
94 int BarResult = bar();
95 int BazResult = baz();
96 )";
97 const char *PostCode = R"(
98 #include "bar.h"
99 #include "baz.h"
101 int BarResult = bar();
102 int BazResult = baz();
105 std::vector<ClangTidyError> Errors;
106 EXPECT_EQ(PostCode,
107 runCheckOnCode<IncludeCleanerCheck>(
108 PreCode, &Errors, "file.cpp", std::nullopt, ClangTidyOptions(),
109 {{"bar.h", R"(#pragma once
110 #include "baz.h"
111 int bar();
112 )"},
113 {"baz.h", R"(#pragma once
114 int baz();
115 )"}}));
118 TEST(IncludeCleanerCheckTest, DedupsMissingIncludes) {
119 llvm::Annotations Code(R"(
120 #include "baz.h" // IWYU pragma: keep
122 int BarResult1 = $diag1^bar();
123 int BarResult2 = $diag2^bar();)");
126 std::vector<ClangTidyError> Errors;
127 runCheckOnCode<IncludeCleanerCheck>(Code.code(), &Errors, "file.cpp",
128 std::nullopt, ClangTidyOptions(),
129 {{"baz.h", R"(#pragma once
130 #include "bar.h"
131 )"},
132 {"bar.h", R"(#pragma once
133 int bar();
134 )"}});
135 ASSERT_THAT(Errors.size(), testing::Eq(1U));
136 EXPECT_EQ(Errors.front().Message.Message,
137 "no header providing \"bar\" is directly included");
138 EXPECT_EQ(Errors.front().Message.FileOffset, Code.point("diag1"));
141 std::vector<ClangTidyError> Errors;
142 ClangTidyOptions Opts;
143 Opts.CheckOptions.insert({"DeduplicateFindings", "false"});
144 runCheckOnCode<IncludeCleanerCheck>(Code.code(), &Errors, "file.cpp",
145 std::nullopt, Opts,
146 {{"baz.h", R"(#pragma once
147 #include "bar.h"
148 )"},
149 {"bar.h", R"(#pragma once
150 int bar();
151 )"}});
152 ASSERT_THAT(Errors.size(), testing::Eq(2U));
153 EXPECT_EQ(Errors.front().Message.Message,
154 "no header providing \"bar\" is directly included");
155 EXPECT_EQ(Errors.front().Message.FileOffset, Code.point("diag1"));
156 EXPECT_EQ(Errors.back().Message.Message,
157 "no header providing \"bar\" is directly included");
158 EXPECT_EQ(Errors.back().Message.FileOffset, Code.point("diag2"));
162 TEST(IncludeCleanerCheckTest, SuppressMissingIncludes) {
163 const char *PreCode = R"(
164 #include "bar.h"
166 int BarResult = bar();
167 int BazResult = baz();
168 int QuxResult = qux();
169 int PrivResult = test();
170 std::vector x;
173 ClangTidyOptions Opts;
174 Opts.CheckOptions["IgnoreHeaders"] = llvm::StringRef{
175 "public.h;<vector>;baz.h;" +
176 llvm::Regex::escape(appendPathFileSystemIndependent({"foo", "qux.h"}))};
177 std::vector<ClangTidyError> Errors;
178 EXPECT_EQ(PreCode, runCheckOnCode<IncludeCleanerCheck>(
179 PreCode, &Errors, "file.cpp", std::nullopt, Opts,
180 {{"bar.h", R"(#pragma once
181 #include "baz.h"
182 #include "foo/qux.h"
183 #include "private.h"
184 int bar();
185 namespace std { struct vector {}; }
186 )"},
187 {"baz.h", R"(#pragma once
188 int baz();
189 )"},
190 {"private.h", R"(#pragma once
191 // IWYU pragma: private, include "public.h"
192 int test();
193 )"},
194 {appendPathFileSystemIndependent({"foo", "qux.h"}),
195 R"(#pragma once
196 int qux();
197 )"}}));
200 TEST(IncludeCleanerCheckTest, MultipleTimeMissingInclude) {
201 const char *PreCode = R"(
202 #include "bar.h"
204 int BarResult = bar();
205 int BazResult_0 = baz_0();
206 int BazResult_1 = baz_1();
208 const char *PostCode = R"(
209 #include "bar.h"
210 #include "baz.h"
212 int BarResult = bar();
213 int BazResult_0 = baz_0();
214 int BazResult_1 = baz_1();
217 std::vector<ClangTidyError> Errors;
218 EXPECT_EQ(PostCode,
219 runCheckOnCode<IncludeCleanerCheck>(
220 PreCode, &Errors, "file.cpp", std::nullopt, 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 )"}}));
231 TEST(IncludeCleanerCheckTest, SystemMissingIncludes) {
232 const char *PreCode = R"(
233 #include <vector>
235 std::string HelloString;
236 std::vector Vec;
238 const char *PostCode = R"(
239 #include <string>
240 #include <vector>
242 std::string HelloString;
243 std::vector Vec;
246 std::vector<ClangTidyError> Errors;
247 EXPECT_EQ(PostCode,
248 runCheckOnCode<IncludeCleanerCheck>(
249 PreCode, &Errors, "file.cpp", std::nullopt, ClangTidyOptions(),
250 {{"string", R"(#pragma once
251 namespace std { class string {}; }
252 )"},
253 {"vector", R"(#pragma once
254 #include <string>
255 namespace std { class vector {}; }
256 )"}}));
259 TEST(IncludeCleanerCheckTest, PragmaMissingIncludes) {
260 const char *PreCode = R"(
261 #include "bar.h"
263 int BarResult = bar();
264 int FooBarResult = foobar();
266 const char *PostCode = R"(
267 #include "bar.h"
268 #include "public.h"
270 int BarResult = bar();
271 int FooBarResult = foobar();
274 std::vector<ClangTidyError> Errors;
275 EXPECT_EQ(PostCode,
276 runCheckOnCode<IncludeCleanerCheck>(
277 PreCode, &Errors, "file.cpp", std::nullopt, ClangTidyOptions(),
278 {{"bar.h", R"(#pragma once
279 #include "private.h"
280 int bar();
281 )"},
282 {"private.h", R"(#pragma once
283 // IWYU pragma: private, include "public.h"
284 int foobar();
285 )"}}));
288 TEST(IncludeCleanerCheckTest, DeclFromMacroExpansion) {
289 const char *PreCode = R"(
290 #include "foo.h"
292 DECLARE(myfunc) {
293 int a;
297 std::vector<ClangTidyError> Errors;
298 EXPECT_EQ(PreCode,
299 runCheckOnCode<IncludeCleanerCheck>(
300 PreCode, &Errors, "file.cpp", std::nullopt, ClangTidyOptions(),
301 {{"foo.h",
302 R"(#pragma once
303 #define DECLARE(X) void X()
304 )"}}));
306 PreCode = R"(
307 #include "foo.h"
309 DECLARE {
310 int a;
314 EXPECT_EQ(PreCode,
315 runCheckOnCode<IncludeCleanerCheck>(
316 PreCode, &Errors, "file.cpp", std::nullopt, ClangTidyOptions(),
317 {{"foo.h",
318 R"(#pragma once
319 #define DECLARE void myfunc()
320 )"}}));
323 } // namespace
324 } // namespace test
325 } // namespace tidy
326 } // namespace clang