1 //===--- IncludeCleanerTest.cpp - clang-tidy -----------------------------===//
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
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>
26 using namespace clang::tidy::misc
;
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
"(
47 const char *PostCode
= "\n";
49 std::vector
<ClangTidyError
> Errors
;
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
"(
60 #include "baz
/qux
/qux
.h
"
65 const char *PostCode
= R
"(
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"})))};
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"}),
90 TEST(IncludeCleanerCheckTest
, BasicMissingIncludes
) {
91 const char *PreCode
= R
"(
94 int BarResult = bar();
95 int BazResult = baz();
97 const char *PostCode
= R
"(
101 int BarResult = bar();
102 int BazResult = baz();
105 std::vector
<ClangTidyError
> Errors
;
107 runCheckOnCode
<IncludeCleanerCheck
>(
108 PreCode
, &Errors
, "file.cpp", std::nullopt
, ClangTidyOptions(),
109 {{"bar.h", R
"(#pragma once
113 {"baz.h", R
"(#pragma once
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
132 {"bar.h", R
"(#pragma once
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",
146 {{"baz.h", R
"(#pragma once
149 {"bar.h", R
"(#pragma once
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
"(
166 int BarResult = bar();
167 int BazResult = baz();
168 int QuxResult = qux();
169 int PrivResult = test();
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
185 namespace std { struct vector {}; }
187 {"baz.h", R
"(#pragma once
190 {"private.h", R
"(#pragma once
191 // IWYU pragma: private, include "public.h
"
194 {appendPathFileSystemIndependent({"foo", "qux.h"}),
200 TEST(IncludeCleanerCheckTest
, MultipleTimeMissingInclude
) {
201 const char *PreCode
= R
"(
204 int BarResult = bar();
205 int BazResult_0 = baz_0();
206 int BazResult_1 = baz_1();
208 const char *PostCode
= R
"(
212 int BarResult = bar();
213 int BazResult_0 = baz_0();
214 int BazResult_1 = baz_1();
217 std::vector
<ClangTidyError
> Errors
;
219 runCheckOnCode
<IncludeCleanerCheck
>(
220 PreCode
, &Errors
, "file.cpp", std::nullopt
, ClangTidyOptions(),
221 {{"bar.h", R
"(#pragma once
225 {"baz.h", R
"(#pragma once
231 TEST(IncludeCleanerCheckTest
, SystemMissingIncludes
) {
232 const char *PreCode
= R
"(
235 std::string HelloString;
238 const char *PostCode
= R
"(
242 std::string HelloString;
246 std::vector
<ClangTidyError
> Errors
;
248 runCheckOnCode
<IncludeCleanerCheck
>(
249 PreCode
, &Errors
, "file.cpp", std::nullopt
, ClangTidyOptions(),
250 {{"string", R
"(#pragma once
251 namespace std { class string {}; }
253 {"vector", R
"(#pragma once
255 namespace std { class vector {}; }
259 TEST(IncludeCleanerCheckTest
, PragmaMissingIncludes
) {
260 const char *PreCode
= R
"(
263 int BarResult = bar();
264 int FooBarResult = foobar();
266 const char *PostCode
= R
"(
270 int BarResult = bar();
271 int FooBarResult = foobar();
274 std::vector
<ClangTidyError
> Errors
;
276 runCheckOnCode
<IncludeCleanerCheck
>(
277 PreCode
, &Errors
, "file.cpp", std::nullopt
, ClangTidyOptions(),
278 {{"bar.h", R
"(#pragma once
282 {"private.h", R
"(#pragma once
283 // IWYU pragma: private, include "public.h
"
288 TEST(IncludeCleanerCheckTest
, DeclFromMacroExpansion
) {
289 const char *PreCode
= R
"(
297 std::vector
<ClangTidyError
> Errors
;
299 runCheckOnCode
<IncludeCleanerCheck
>(
300 PreCode
, &Errors
, "file.cpp", std::nullopt
, ClangTidyOptions(),
303 #define DECLARE(X) void X()
315 runCheckOnCode
<IncludeCleanerCheck
>(
316 PreCode
, &Errors
, "file.cpp", std::nullopt
, ClangTidyOptions(),
319 #define DECLARE void myfunc()