xref: /llvm-project/clang-tools-extra/unittests/clang-tidy/IncludeCleanerTest.cpp (revision 222dd235ffc39b3695a3c002593097bec216a8fa)
1c28506baSViktoriia Bakalova //===--- IncludeCleanerTest.cpp - clang-tidy -----------------------------===//
2c28506baSViktoriia Bakalova //
3c28506baSViktoriia Bakalova // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c28506baSViktoriia Bakalova // See https://llvm.org/LICENSE.txt for license information.
5c28506baSViktoriia Bakalova // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6c28506baSViktoriia Bakalova //
7c28506baSViktoriia Bakalova //===----------------------------------------------------------------------===//
8c28506baSViktoriia Bakalova 
9c28506baSViktoriia Bakalova #include "ClangTidyDiagnosticConsumer.h"
10c28506baSViktoriia Bakalova #include "ClangTidyOptions.h"
11c28506baSViktoriia Bakalova #include "ClangTidyTest.h"
12c28506baSViktoriia Bakalova #include "misc/IncludeCleanerCheck.h"
13c28506baSViktoriia Bakalova #include "llvm/ADT/SmallString.h"
14c28506baSViktoriia Bakalova #include "llvm/ADT/StringRef.h"
15c28506baSViktoriia Bakalova #include "llvm/Support/FormatVariadic.h"
16c28506baSViktoriia Bakalova #include "llvm/Support/Path.h"
17c28506baSViktoriia Bakalova #include "llvm/Support/Regex.h"
1889d0a76bSKadir Cetinkaya #include "llvm/Testing/Annotations/Annotations.h"
1989d0a76bSKadir Cetinkaya #include "gmock/gmock.h"
20c28506baSViktoriia Bakalova #include "gtest/gtest.h"
21c28506baSViktoriia Bakalova #include <initializer_list>
22c28506baSViktoriia Bakalova 
23c28506baSViktoriia Bakalova #include <optional>
24c28506baSViktoriia Bakalova #include <vector>
25c28506baSViktoriia Bakalova 
26c28506baSViktoriia Bakalova using namespace clang::tidy::misc;
27c28506baSViktoriia Bakalova 
28c28506baSViktoriia Bakalova namespace clang {
29c28506baSViktoriia Bakalova namespace tidy {
30c28506baSViktoriia Bakalova namespace test {
31c28506baSViktoriia Bakalova namespace {
32c28506baSViktoriia Bakalova 
33c28506baSViktoriia Bakalova std::string
34c28506baSViktoriia Bakalova appendPathFileSystemIndependent(std::initializer_list<std::string> Segments) {
35c28506baSViktoriia Bakalova   llvm::SmallString<32> Result;
36c28506baSViktoriia Bakalova   for (const auto &Segment : Segments)
37c28506baSViktoriia Bakalova     llvm::sys::path::append(Result, llvm::sys::path::Style::native, Segment);
38c28506baSViktoriia Bakalova   return std::string(Result.str());
39c28506baSViktoriia Bakalova }
40c28506baSViktoriia Bakalova 
41c28506baSViktoriia Bakalova TEST(IncludeCleanerCheckTest, BasicUnusedIncludes) {
42c28506baSViktoriia Bakalova   const char *PreCode = R"(
43c28506baSViktoriia Bakalova #include "bar.h"
44c28506baSViktoriia Bakalova #include <vector>
45c28506baSViktoriia Bakalova #include "bar.h"
46c28506baSViktoriia Bakalova )";
47c28506baSViktoriia Bakalova   const char *PostCode = "\n";
48c28506baSViktoriia Bakalova 
49c28506baSViktoriia Bakalova   std::vector<ClangTidyError> Errors;
50d3cfc712Skadir çetinkaya   EXPECT_EQ(PostCode,
51d3cfc712Skadir çetinkaya             runCheckOnCode<IncludeCleanerCheck>(
52fc5de0afSJay Foad                 PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),
53d3cfc712Skadir çetinkaya                 {{"bar.h", "#pragma once"}, {"vector", "#pragma once"}}));
54c28506baSViktoriia Bakalova }
55c28506baSViktoriia Bakalova 
56c28506baSViktoriia Bakalova TEST(IncludeCleanerCheckTest, SuppressUnusedIncludes) {
57c28506baSViktoriia Bakalova   const char *PreCode = R"(
58c28506baSViktoriia Bakalova #include "bar.h"
59c28506baSViktoriia Bakalova #include "foo/qux.h"
60c28506baSViktoriia Bakalova #include "baz/qux/qux.h"
61c28506baSViktoriia Bakalova #include <vector>
6277feba5aSkadir çetinkaya #include <list>
63c28506baSViktoriia Bakalova )";
64c28506baSViktoriia Bakalova 
65c28506baSViktoriia Bakalova   const char *PostCode = R"(
66c28506baSViktoriia Bakalova #include "bar.h"
67c28506baSViktoriia Bakalova #include "foo/qux.h"
68c28506baSViktoriia Bakalova #include <vector>
6977feba5aSkadir çetinkaya #include <list>
70c28506baSViktoriia Bakalova )";
71c28506baSViktoriia Bakalova 
72c28506baSViktoriia Bakalova   std::vector<ClangTidyError> Errors;
73c28506baSViktoriia Bakalova   ClangTidyOptions Opts;
74*222dd235SCongcong Cai   Opts.CheckOptions["test-check-0.IgnoreHeaders"] = llvm::StringRef{
75*222dd235SCongcong Cai       llvm::formatv("bar.h;{0};{1};vector;<list>;",
76*222dd235SCongcong Cai                     llvm::Regex::escape(
77*222dd235SCongcong Cai                         appendPathFileSystemIndependent({"foo", "qux.h"})),
78*222dd235SCongcong Cai                     llvm::Regex::escape(
79*222dd235SCongcong Cai                         appendPathFileSystemIndependent({"baz", "qux"})))};
80c28506baSViktoriia Bakalova   EXPECT_EQ(
81c28506baSViktoriia Bakalova       PostCode,
82c28506baSViktoriia Bakalova       runCheckOnCode<IncludeCleanerCheck>(
83fc5de0afSJay Foad           PreCode, &Errors, "file.cpp", {}, Opts,
84d3cfc712Skadir çetinkaya           {{"bar.h", "#pragma once"},
85d3cfc712Skadir çetinkaya            {"vector", "#pragma once"},
8677feba5aSkadir çetinkaya            {"list", "#pragma once"},
87d3cfc712Skadir çetinkaya            {appendPathFileSystemIndependent({"foo", "qux.h"}), "#pragma once"},
88d3cfc712Skadir çetinkaya            {appendPathFileSystemIndependent({"baz", "qux", "qux.h"}),
89d3cfc712Skadir çetinkaya             "#pragma once"}}));
90c28506baSViktoriia Bakalova }
91c28506baSViktoriia Bakalova 
92c28506baSViktoriia Bakalova TEST(IncludeCleanerCheckTest, BasicMissingIncludes) {
93c28506baSViktoriia Bakalova   const char *PreCode = R"(
94c28506baSViktoriia Bakalova #include "bar.h"
95c28506baSViktoriia Bakalova 
96c28506baSViktoriia Bakalova int BarResult = bar();
97c28506baSViktoriia Bakalova int BazResult = baz();
98c28506baSViktoriia Bakalova )";
99c28506baSViktoriia Bakalova   const char *PostCode = R"(
100c28506baSViktoriia Bakalova #include "bar.h"
101c28506baSViktoriia Bakalova #include "baz.h"
102c28506baSViktoriia Bakalova 
103c28506baSViktoriia Bakalova int BarResult = bar();
104c28506baSViktoriia Bakalova int BazResult = baz();
105c28506baSViktoriia Bakalova )";
106c28506baSViktoriia Bakalova 
107c28506baSViktoriia Bakalova   std::vector<ClangTidyError> Errors;
108fc5de0afSJay Foad   EXPECT_EQ(PostCode, runCheckOnCode<IncludeCleanerCheck>(
109fc5de0afSJay Foad                           PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),
110c28506baSViktoriia Bakalova                           {{"bar.h", R"(#pragma once
111c28506baSViktoriia Bakalova                               #include "baz.h"
112c28506baSViktoriia Bakalova                               int bar();
113c28506baSViktoriia Bakalova                            )"},
114c28506baSViktoriia Bakalova                            {"baz.h", R"(#pragma once
115c28506baSViktoriia Bakalova                               int baz();
116c28506baSViktoriia Bakalova                            )"}}));
117c28506baSViktoriia Bakalova }
118c28506baSViktoriia Bakalova 
11989d0a76bSKadir Cetinkaya TEST(IncludeCleanerCheckTest, DedupsMissingIncludes) {
12089d0a76bSKadir Cetinkaya   llvm::Annotations Code(R"(
12189d0a76bSKadir Cetinkaya #include "baz.h" // IWYU pragma: keep
12289d0a76bSKadir Cetinkaya 
12389d0a76bSKadir Cetinkaya int BarResult1 = $diag1^bar();
12489d0a76bSKadir Cetinkaya int BarResult2 = $diag2^bar();)");
12589d0a76bSKadir Cetinkaya 
12689d0a76bSKadir Cetinkaya   {
12789d0a76bSKadir Cetinkaya     std::vector<ClangTidyError> Errors;
128fc5de0afSJay Foad     runCheckOnCode<IncludeCleanerCheck>(Code.code(), &Errors, "file.cpp", {},
129fc5de0afSJay Foad                                         ClangTidyOptions(),
13089d0a76bSKadir Cetinkaya                                         {{"baz.h", R"(#pragma once
13189d0a76bSKadir Cetinkaya                               #include "bar.h"
13289d0a76bSKadir Cetinkaya                            )"},
13389d0a76bSKadir Cetinkaya                                          {"bar.h", R"(#pragma once
13489d0a76bSKadir Cetinkaya                               int bar();
13589d0a76bSKadir Cetinkaya                            )"}});
13689d0a76bSKadir Cetinkaya     ASSERT_THAT(Errors.size(), testing::Eq(1U));
13789d0a76bSKadir Cetinkaya     EXPECT_EQ(Errors.front().Message.Message,
13889d0a76bSKadir Cetinkaya               "no header providing \"bar\" is directly included");
13989d0a76bSKadir Cetinkaya     EXPECT_EQ(Errors.front().Message.FileOffset, Code.point("diag1"));
14089d0a76bSKadir Cetinkaya   }
14189d0a76bSKadir Cetinkaya   {
14289d0a76bSKadir Cetinkaya     std::vector<ClangTidyError> Errors;
14389d0a76bSKadir Cetinkaya     ClangTidyOptions Opts;
144*222dd235SCongcong Cai     Opts.CheckOptions["test-check-0.DeduplicateFindings"] = "false";
145fc5de0afSJay Foad     runCheckOnCode<IncludeCleanerCheck>(Code.code(), &Errors, "file.cpp", {},
146fc5de0afSJay Foad                                         Opts,
14789d0a76bSKadir Cetinkaya                                         {{"baz.h", R"(#pragma once
14889d0a76bSKadir Cetinkaya                               #include "bar.h"
14989d0a76bSKadir Cetinkaya                            )"},
15089d0a76bSKadir Cetinkaya                                          {"bar.h", R"(#pragma once
15189d0a76bSKadir Cetinkaya                               int bar();
15289d0a76bSKadir Cetinkaya                            )"}});
15389d0a76bSKadir Cetinkaya     ASSERT_THAT(Errors.size(), testing::Eq(2U));
15489d0a76bSKadir Cetinkaya     EXPECT_EQ(Errors.front().Message.Message,
15589d0a76bSKadir Cetinkaya               "no header providing \"bar\" is directly included");
15689d0a76bSKadir Cetinkaya     EXPECT_EQ(Errors.front().Message.FileOffset, Code.point("diag1"));
15789d0a76bSKadir Cetinkaya     EXPECT_EQ(Errors.back().Message.Message,
15889d0a76bSKadir Cetinkaya               "no header providing \"bar\" is directly included");
15989d0a76bSKadir Cetinkaya     EXPECT_EQ(Errors.back().Message.FileOffset, Code.point("diag2"));
16089d0a76bSKadir Cetinkaya   }
16189d0a76bSKadir Cetinkaya }
16289d0a76bSKadir Cetinkaya 
163c28506baSViktoriia Bakalova TEST(IncludeCleanerCheckTest, SuppressMissingIncludes) {
164c28506baSViktoriia Bakalova   const char *PreCode = R"(
165c28506baSViktoriia Bakalova #include "bar.h"
166c28506baSViktoriia Bakalova 
167c28506baSViktoriia Bakalova int BarResult = bar();
168c28506baSViktoriia Bakalova int BazResult = baz();
169c28506baSViktoriia Bakalova int QuxResult = qux();
17077feba5aSkadir çetinkaya int PrivResult = test();
17177feba5aSkadir çetinkaya std::vector x;
172c28506baSViktoriia Bakalova )";
173c28506baSViktoriia Bakalova 
174c28506baSViktoriia Bakalova   ClangTidyOptions Opts;
175*222dd235SCongcong Cai   Opts.CheckOptions["test-check-0.IgnoreHeaders"] = llvm::StringRef{
17677feba5aSkadir çetinkaya       "public.h;<vector>;baz.h;" +
177c28506baSViktoriia Bakalova       llvm::Regex::escape(appendPathFileSystemIndependent({"foo", "qux.h"}))};
178c28506baSViktoriia Bakalova   std::vector<ClangTidyError> Errors;
179c28506baSViktoriia Bakalova   EXPECT_EQ(PreCode, runCheckOnCode<IncludeCleanerCheck>(
180fc5de0afSJay Foad                          PreCode, &Errors, "file.cpp", {}, Opts,
181c28506baSViktoriia Bakalova                          {{"bar.h", R"(#pragma once
182c28506baSViktoriia Bakalova                               #include "baz.h"
183c28506baSViktoriia Bakalova                               #include "foo/qux.h"
18477feba5aSkadir çetinkaya                               #include "private.h"
185c28506baSViktoriia Bakalova                               int bar();
18677feba5aSkadir çetinkaya                               namespace std { struct vector {}; }
187c28506baSViktoriia Bakalova                            )"},
188c28506baSViktoriia Bakalova                           {"baz.h", R"(#pragma once
189c28506baSViktoriia Bakalova                               int baz();
190c28506baSViktoriia Bakalova                            )"},
19177feba5aSkadir çetinkaya                           {"private.h", R"(#pragma once
19277feba5aSkadir çetinkaya                               // IWYU pragma: private, include "public.h"
19377feba5aSkadir çetinkaya                               int test();
19477feba5aSkadir çetinkaya                            )"},
195c28506baSViktoriia Bakalova                           {appendPathFileSystemIndependent({"foo", "qux.h"}),
196c28506baSViktoriia Bakalova                            R"(#pragma once
197c28506baSViktoriia Bakalova                               int qux();
198c28506baSViktoriia Bakalova                            )"}}));
199c28506baSViktoriia Bakalova }
200c28506baSViktoriia Bakalova 
201b0831c39SCongcong Cai TEST(IncludeCleanerCheckTest, MultipleTimeMissingInclude) {
202b0831c39SCongcong Cai   const char *PreCode = R"(
203b0831c39SCongcong Cai #include "bar.h"
204b0831c39SCongcong Cai 
205b0831c39SCongcong Cai int BarResult = bar();
206b0831c39SCongcong Cai int BazResult_0 = baz_0();
207b0831c39SCongcong Cai int BazResult_1 = baz_1();
208b0831c39SCongcong Cai )";
209b0831c39SCongcong Cai   const char *PostCode = R"(
210b0831c39SCongcong Cai #include "bar.h"
211b0831c39SCongcong Cai #include "baz.h"
212b0831c39SCongcong Cai 
213b0831c39SCongcong Cai int BarResult = bar();
214b0831c39SCongcong Cai int BazResult_0 = baz_0();
215b0831c39SCongcong Cai int BazResult_1 = baz_1();
216b0831c39SCongcong Cai )";
217b0831c39SCongcong Cai 
218b0831c39SCongcong Cai   std::vector<ClangTidyError> Errors;
219fc5de0afSJay Foad   EXPECT_EQ(PostCode, runCheckOnCode<IncludeCleanerCheck>(
220fc5de0afSJay Foad                           PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),
221b0831c39SCongcong Cai                           {{"bar.h", R"(#pragma once
222b0831c39SCongcong Cai                               #include "baz.h"
223b0831c39SCongcong Cai                               int bar();
224b0831c39SCongcong Cai                            )"},
225b0831c39SCongcong Cai                            {"baz.h", R"(#pragma once
226b0831c39SCongcong Cai                               int baz_0();
227b0831c39SCongcong Cai                               int baz_1();
228b0831c39SCongcong Cai                            )"}}));
229b0831c39SCongcong Cai }
230b0831c39SCongcong Cai 
231c28506baSViktoriia Bakalova TEST(IncludeCleanerCheckTest, SystemMissingIncludes) {
232c28506baSViktoriia Bakalova   const char *PreCode = R"(
233c28506baSViktoriia Bakalova #include <vector>
234c28506baSViktoriia Bakalova 
235c28506baSViktoriia Bakalova std::string HelloString;
236c28506baSViktoriia Bakalova std::vector Vec;
237c28506baSViktoriia Bakalova )";
238c28506baSViktoriia Bakalova   const char *PostCode = R"(
239c28506baSViktoriia Bakalova #include <string>
240c28506baSViktoriia Bakalova #include <vector>
241c28506baSViktoriia Bakalova 
242c28506baSViktoriia Bakalova std::string HelloString;
243c28506baSViktoriia Bakalova std::vector Vec;
244c28506baSViktoriia Bakalova )";
245c28506baSViktoriia Bakalova 
246c28506baSViktoriia Bakalova   std::vector<ClangTidyError> Errors;
247fc5de0afSJay Foad   EXPECT_EQ(PostCode, runCheckOnCode<IncludeCleanerCheck>(
248fc5de0afSJay Foad                           PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),
249c28506baSViktoriia Bakalova                           {{"string", R"(#pragma once
250c28506baSViktoriia Bakalova                               namespace std { class string {}; }
251c28506baSViktoriia Bakalova                             )"},
252c28506baSViktoriia Bakalova                            {"vector", R"(#pragma once
253c28506baSViktoriia Bakalova                               #include <string>
254c28506baSViktoriia Bakalova                               namespace std { class vector {}; }
255c28506baSViktoriia Bakalova                             )"}}));
256c28506baSViktoriia Bakalova }
257c28506baSViktoriia Bakalova 
258c28506baSViktoriia Bakalova TEST(IncludeCleanerCheckTest, PragmaMissingIncludes) {
259c28506baSViktoriia Bakalova   const char *PreCode = R"(
260c28506baSViktoriia Bakalova #include "bar.h"
261c28506baSViktoriia Bakalova 
262c28506baSViktoriia Bakalova int BarResult = bar();
263c28506baSViktoriia Bakalova int FooBarResult = foobar();
264c28506baSViktoriia Bakalova )";
265c28506baSViktoriia Bakalova   const char *PostCode = R"(
266c28506baSViktoriia Bakalova #include "bar.h"
267c28506baSViktoriia Bakalova #include "public.h"
268c28506baSViktoriia Bakalova 
269c28506baSViktoriia Bakalova int BarResult = bar();
270c28506baSViktoriia Bakalova int FooBarResult = foobar();
271c28506baSViktoriia Bakalova )";
272c28506baSViktoriia Bakalova 
273c28506baSViktoriia Bakalova   std::vector<ClangTidyError> Errors;
274fc5de0afSJay Foad   EXPECT_EQ(PostCode, runCheckOnCode<IncludeCleanerCheck>(
275fc5de0afSJay Foad                           PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),
276c28506baSViktoriia Bakalova                           {{"bar.h", R"(#pragma once
277c28506baSViktoriia Bakalova                               #include "private.h"
278c28506baSViktoriia Bakalova                               int bar();
279c28506baSViktoriia Bakalova                            )"},
280c28506baSViktoriia Bakalova                            {"private.h", R"(#pragma once
281c28506baSViktoriia Bakalova                                 // IWYU pragma: private, include "public.h"
282c28506baSViktoriia Bakalova                                 int foobar();
283c28506baSViktoriia Bakalova                                )"}}));
284c28506baSViktoriia Bakalova }
285c28506baSViktoriia Bakalova 
286c28506baSViktoriia Bakalova TEST(IncludeCleanerCheckTest, DeclFromMacroExpansion) {
287c28506baSViktoriia Bakalova   const char *PreCode = R"(
288c28506baSViktoriia Bakalova #include "foo.h"
289c28506baSViktoriia Bakalova 
290c28506baSViktoriia Bakalova DECLARE(myfunc) {
291c28506baSViktoriia Bakalova    int a;
292c28506baSViktoriia Bakalova }
293c28506baSViktoriia Bakalova )";
294c28506baSViktoriia Bakalova 
295c28506baSViktoriia Bakalova   std::vector<ClangTidyError> Errors;
296fc5de0afSJay Foad   EXPECT_EQ(PreCode, runCheckOnCode<IncludeCleanerCheck>(
297fc5de0afSJay Foad                          PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),
298c28506baSViktoriia Bakalova                          {{"foo.h",
299c28506baSViktoriia Bakalova                            R"(#pragma once
300c28506baSViktoriia Bakalova                      #define DECLARE(X) void X()
301c28506baSViktoriia Bakalova                   )"}}));
302c28506baSViktoriia Bakalova 
303c28506baSViktoriia Bakalova   PreCode = R"(
304c28506baSViktoriia Bakalova #include "foo.h"
305c28506baSViktoriia Bakalova 
306c28506baSViktoriia Bakalova DECLARE {
307c28506baSViktoriia Bakalova    int a;
308c28506baSViktoriia Bakalova }
309c28506baSViktoriia Bakalova )";
310c28506baSViktoriia Bakalova 
311fc5de0afSJay Foad   EXPECT_EQ(PreCode, runCheckOnCode<IncludeCleanerCheck>(
312fc5de0afSJay Foad                          PreCode, &Errors, "file.cpp", {}, ClangTidyOptions(),
313c28506baSViktoriia Bakalova                          {{"foo.h",
314c28506baSViktoriia Bakalova                            R"(#pragma once
315c28506baSViktoriia Bakalova                      #define DECLARE void myfunc()
316c28506baSViktoriia Bakalova                   )"}}));
317c28506baSViktoriia Bakalova }
318c28506baSViktoriia Bakalova 
319c28506baSViktoriia Bakalova } // namespace
320c28506baSViktoriia Bakalova } // namespace test
321c28506baSViktoriia Bakalova } // namespace tidy
322c28506baSViktoriia Bakalova } // namespace clang
323