1 //===---- IncludeInserterTest.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 "../clang-tidy/utils/IncludeInserter.h"
10 #include "clang/Lex/Preprocessor.h"
11 #include "clang/Frontend/CompilerInstance.h"
12 #include "ClangTidyTest.h"
13 #include "gtest/gtest.h"
15 // FIXME: Canonicalize paths correctly on windows.
16 // Currently, adding virtual files will canonicalize the paths before
17 // storing the virtual entries.
18 // When resolving virtual entries in the FileManager, the paths (for
19 // example coming from a #include directive) are not canonicalized
20 // to native paths; thus, the virtual file is not found.
21 // This needs to be fixed in the FileManager before we can make
22 // clang-tidy tests work.
29 class IncludeInserterCheckBase
: public ClangTidyCheck
{
31 IncludeInserterCheckBase(StringRef CheckName
, ClangTidyContext
*Context
,
32 utils::IncludeSorter::IncludeStyle Style
=
33 utils::IncludeSorter::IS_Google
,
34 bool SelfContainedDiags
= false)
35 : ClangTidyCheck(CheckName
, Context
),
36 Inserter(Style
, SelfContainedDiags
) {}
38 void registerPPCallbacks(const SourceManager
&SM
, Preprocessor
*PP
,
39 Preprocessor
*ModuleExpanderPP
) override
{
40 Inserter
.registerPreprocessor(PP
);
43 void registerMatchers(ast_matchers::MatchFinder
*Finder
) override
{
44 Finder
->addMatcher(ast_matchers::declStmt().bind("stmt"), this);
47 void check(const ast_matchers::MatchFinder::MatchResult
&Result
) override
{
48 auto Diag
= diag(Result
.Nodes
.getNodeAs
<DeclStmt
>("stmt")->getBeginLoc(),
50 for (StringRef Header
: headersToInclude()) {
51 Diag
<< Inserter
.createMainFileIncludeInsertion(Header
);
55 virtual std::vector
<StringRef
> headersToInclude() const = 0;
57 utils::IncludeInserter Inserter
;
60 class NonSystemHeaderInserterCheck
: public IncludeInserterCheckBase
{
62 NonSystemHeaderInserterCheck(StringRef CheckName
, ClangTidyContext
*Context
)
63 : IncludeInserterCheckBase(CheckName
, Context
) {}
65 std::vector
<StringRef
> headersToInclude() const override
{
66 return {"path/to/header.h"};
70 class EarlyInAlphabetHeaderInserterCheck
: public IncludeInserterCheckBase
{
72 EarlyInAlphabetHeaderInserterCheck(StringRef CheckName
, ClangTidyContext
*Context
)
73 : IncludeInserterCheckBase(CheckName
, Context
) {}
75 std::vector
<StringRef
> headersToInclude() const override
{
76 return {"a/header.h"};
80 class MultipleHeaderInserterCheck
: public IncludeInserterCheckBase
{
82 MultipleHeaderInserterCheck(StringRef CheckName
, ClangTidyContext
*Context
)
83 : IncludeInserterCheckBase(CheckName
, Context
) {}
85 std::vector
<StringRef
> headersToInclude() const override
{
86 return {"path/to/header.h", "path/to/header2.h", "path/to/header.h"};
90 class MultipleHeaderSingleInserterCheck
: public IncludeInserterCheckBase
{
92 MultipleHeaderSingleInserterCheck(StringRef CheckName
,
93 ClangTidyContext
*Context
)
94 : IncludeInserterCheckBase(CheckName
, Context
,
95 utils::IncludeSorter::IS_Google
,
96 /*SelfContainedDiags=*/true) {}
98 std::vector
<StringRef
> headersToInclude() const override
{
99 return {"path/to/header.h", "path/to/header2.h", "path/to/header.h"};
103 class CSystemIncludeInserterCheck
: public IncludeInserterCheckBase
{
105 CSystemIncludeInserterCheck(StringRef CheckName
, ClangTidyContext
*Context
)
106 : IncludeInserterCheckBase(CheckName
, Context
) {}
108 std::vector
<StringRef
> headersToInclude() const override
{
109 return {"<stdlib.h>"};
113 class CXXSystemIncludeInserterCheck
: public IncludeInserterCheckBase
{
115 CXXSystemIncludeInserterCheck(StringRef CheckName
, ClangTidyContext
*Context
)
116 : IncludeInserterCheckBase(CheckName
, Context
) {}
118 std::vector
<StringRef
> headersToInclude() const override
{ return {"<set>"}; }
121 class InvalidIncludeInserterCheck
: public IncludeInserterCheckBase
{
123 InvalidIncludeInserterCheck(StringRef CheckName
, ClangTidyContext
*Context
)
124 : IncludeInserterCheckBase(CheckName
, Context
) {}
126 std::vector
<StringRef
> headersToInclude() const override
{
127 return {"a.h", "<stdlib.h", "cstdlib>", "b.h", "<c.h>", "<d>"};
131 class ObjCEarlyInAlphabetHeaderInserterCheck
: public IncludeInserterCheckBase
{
133 ObjCEarlyInAlphabetHeaderInserterCheck(StringRef CheckName
,
134 ClangTidyContext
*Context
)
135 : IncludeInserterCheckBase(CheckName
, Context
,
136 utils::IncludeSorter::IS_Google_ObjC
) {}
138 std::vector
<StringRef
> headersToInclude() const override
{
139 return {"a/header.h"};
143 class ObjCCategoryHeaderInserterCheck
: public IncludeInserterCheckBase
{
145 ObjCCategoryHeaderInserterCheck(StringRef CheckName
,
146 ClangTidyContext
*Context
)
147 : IncludeInserterCheckBase(CheckName
, Context
,
148 utils::IncludeSorter::IS_Google_ObjC
) {}
150 std::vector
<StringRef
> headersToInclude() const override
{
151 return {"top_level_test_header+foo.h"};
155 class ObjCGeneratedHeaderInserterCheck
: public IncludeInserterCheckBase
{
157 ObjCGeneratedHeaderInserterCheck(StringRef CheckName
,
158 ClangTidyContext
*Context
)
159 : IncludeInserterCheckBase(CheckName
, Context
,
160 utils::IncludeSorter::IS_Google_ObjC
) {}
162 std::vector
<StringRef
> headersToInclude() const override
{
163 return {"clang_tidy/tests/generated_file.proto.h"};
167 template <typename Check
>
168 std::string
runCheckOnCode(StringRef Code
, StringRef Filename
) {
169 std::vector
<ClangTidyError
> Errors
;
170 return test::runCheckOnCode
<Check
>(Code
, &Errors
, Filename
, std::nullopt
,
172 {// Main file include
174 "insert_includes_test_header.h",
176 // Top-level main file include +
178 {"top_level_test_header.h", "\n"},
179 {"top_level_test_header+foo.h", "\n"},
182 "insert_includes_test_header+foo.h",
184 // Non system headers
185 {"a/header.h", "\n"},
186 {"path/to/a/header.h", "\n"},
187 {"path/to/z/header.h", "\n"},
188 {"path/to/header.h", "\n"},
189 {"path/to/header2.h", "\n"},
192 "generated_file.proto.h",
194 // Fake system headers.
203 TEST(IncludeInserterTest
, InsertAfterLastNonSystemInclude
) {
204 const char *PreCode
= R
"(
205 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
210 #include "path
/to
/a
/header
.h
"
215 const char *PostCode
= R
"(
216 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
221 #include "path
/to
/a
/header
.h
"
222 #include "path
/to
/header
.h
"
229 runCheckOnCode
<NonSystemHeaderInserterCheck
>(
230 PreCode
, "clang_tidy/tests/insert_includes_test_input2.cc"));
233 TEST(IncludeInserterTest
, InsertMultipleIncludesAndDeduplicate
) {
234 const char *PreCode
= R
"(
235 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
240 #include "path
/to
/a
/header
.h
"
245 const char *PostCode
= R
"(
246 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
251 #include "path
/to
/a
/header
.h
"
252 #include "path
/to
/header
.h
"
253 #include "path
/to
/header2
.h
"
260 runCheckOnCode
<MultipleHeaderInserterCheck
>(
261 PreCode
, "clang_tidy/tests/insert_includes_test_input2.cc"));
264 TEST(IncludeInserterTest
, InsertMultipleIncludesNoDeduplicate
) {
265 const char *PreCode
= R
"(
266 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
271 #include "path
/to
/a
/header
.h
"
276 // FIXME: ClangFormat bug - https://bugs.llvm.org/show_bug.cgi?id=49298
278 const char *PostCode
= R
"(
279 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
284 #include "path
/to
/a
/header
.h
"
285 #include "path
/to
/header
.h
"
286 #include "path
/to
/header2
.h
"
287 #include "path
/to
/header
.h
"
295 runCheckOnCode
<MultipleHeaderSingleInserterCheck
>(
296 PreCode
, "clang_tidy/tests/insert_includes_test_input2.cc"));
299 TEST(IncludeInserterTest
, InsertBeforeFirstNonSystemInclude
) {
300 const char *PreCode
= R
"(
301 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
306 #include "path
/to
/z
/header
.h
"
311 const char *PostCode
= R
"(
312 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
317 #include "path
/to
/header
.h
"
318 #include "path
/to
/z
/header
.h
"
325 runCheckOnCode
<NonSystemHeaderInserterCheck
>(
326 PreCode
, "clang_tidy/tests/insert_includes_test_input2.cc"));
329 TEST(IncludeInserterTest
, InsertBetweenNonSystemIncludes
) {
330 const char *PreCode
= R
"(
331 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
336 #include "path
/to
/a
/header
.h
"
337 #include "path
/to
/z
/header
.h
"
342 const char *PostCode
= R
"(
343 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
348 #include "path
/to
/a
/header
.h
"
349 #include "path
/to
/header
.h
"
350 #include "path
/to
/z
/header
.h
"
357 runCheckOnCode
<NonSystemHeaderInserterCheck
>(
358 PreCode
, "clang_tidy/tests/insert_includes_test_input2.cc"));
361 TEST(IncludeInserterTest
, NonSystemIncludeAlreadyIncluded
) {
362 const char *PreCode
= R
"(
363 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
368 #include "path
/to
/a
/header
.h
"
369 #include "path
/to
/header
.h
"
370 #include "path
/to
/z
/header
.h
"
376 runCheckOnCode
<NonSystemHeaderInserterCheck
>(
377 PreCode
, "clang_tidy/tests/insert_includes_test_input2.cc"));
380 TEST(IncludeInserterTest
, InsertNonSystemIncludeAfterLastCXXSystemInclude
) {
381 const char *PreCode
= R
"(
382 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
390 const char *PostCode
= R
"(
391 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
396 #include "path
/to
/header
.h
"
403 runCheckOnCode
<NonSystemHeaderInserterCheck
>(
404 PreCode
, "clang_tidy/tests/insert_includes_test_header.cc"));
407 TEST(IncludeInserterTest
, InsertNonSystemIncludeAfterMainFileInclude
) {
408 const char *PreCode
= R
"(
409 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
414 const char *PostCode
= R
"(
415 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
417 #include "path
/to
/header
.h
"
424 runCheckOnCode
<NonSystemHeaderInserterCheck
>(
425 PreCode
, "clang_tidy/tests/insert_includes_test_header.cc"));
428 TEST(IncludeInserterTest
, InsertCXXSystemIncludeAfterLastCXXSystemInclude
) {
429 const char *PreCode
= R
"(
430 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
435 #include "path
/to
/a
/header
.h
"
440 const char *PostCode
= R
"(
441 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
447 #include "path
/to
/a
/header
.h
"
454 runCheckOnCode
<CXXSystemIncludeInserterCheck
>(
455 PreCode
, "clang_tidy/tests/insert_includes_test_header.cc"));
458 TEST(IncludeInserterTest
, InsertCXXSystemIncludeBeforeFirstCXXSystemInclude
) {
459 const char *PreCode
= R
"(
460 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
464 #include "path
/to
/a
/header
.h
"
469 const char *PostCode
= R
"(
470 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
475 #include "path
/to
/a
/header
.h
"
482 runCheckOnCode
<CXXSystemIncludeInserterCheck
>(
483 PreCode
, "clang_tidy/tests/insert_includes_test_header.cc"));
486 TEST(IncludeInserterTest
, InsertCXXSystemIncludeBetweenCXXSystemIncludes
) {
487 const char *PreCode
= R
"(
488 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
493 #include "path
/to
/a
/header
.h
"
498 const char *PostCode
= R
"(
499 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
505 #include "path
/to
/a
/header
.h
"
512 runCheckOnCode
<CXXSystemIncludeInserterCheck
>(
513 PreCode
, "clang_tidy/tests/insert_includes_test_header.cc"));
516 TEST(IncludeInserterTest
, InsertCXXSystemIncludeAfterMainFileInclude
) {
517 const char *PreCode
= R
"(
518 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
520 #include "path
/to
/a
/header
.h
"
525 const char *PostCode
= R
"(
526 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
530 #include "path
/to
/a
/header
.h
"
537 runCheckOnCode
<CXXSystemIncludeInserterCheck
>(
538 PreCode
, "clang_tidy/tests/insert_includes_test_header.cc"));
541 TEST(IncludeInserterTest
, InsertCXXSystemIncludeAfterCSystemInclude
) {
542 const char *PreCode
= R
"(
543 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
547 #include "path
/to
/a
/header
.h
"
552 const char *PostCode
= R
"(
553 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
559 #include "path
/to
/a
/header
.h
"
566 runCheckOnCode
<CXXSystemIncludeInserterCheck
>(
567 PreCode
, "clang_tidy/tests/insert_includes_test_header.cc"));
570 TEST(IncludeInserterTest
, InsertCXXSystemIncludeBeforeNonSystemInclude
) {
571 const char *PreCode
= R
"(
572 #include "path
/to
/a
/header
.h
"
577 const char *PostCode
= R
"(
580 #include "path
/to
/a
/header
.h
"
588 runCheckOnCode
<CXXSystemIncludeInserterCheck
>(
589 PreCode
, "repo/clang_tidy/tests/insert_includes_test_header.cc"));
592 TEST(IncludeInserterTest
, InsertCSystemIncludeBeforeCXXSystemInclude
) {
593 const char *PreCode
= R
"(
596 #include "path
/to
/a
/header
.h
"
601 const char *PostCode
= R
"(
606 #include "path
/to
/a
/header
.h
"
614 runCheckOnCode
<CSystemIncludeInserterCheck
>(
615 PreCode
, "repo/clang_tidy/tests/insert_includes_test_header.cc"));
618 TEST(IncludeInserterTest
, InsertIncludeIfThereWasNoneBefore
) {
619 const char *PreCode
= R
"(
623 const char *PostCode
= R
"(#include <set>
632 runCheckOnCode
<CXXSystemIncludeInserterCheck
>(
633 PreCode
, "repo/clang_tidy/tests/insert_includes_test_header.cc"));
636 TEST(IncludeInserterTest
, DontInsertDuplicateIncludeEvenIfMiscategorized
) {
637 const char *PreCode
= R
"(
638 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
644 #include "a
/header
.h
"
645 #include "path
/to
/a
/header
.h
"
646 #include "path
/to
/header
.h
"
652 const char *PostCode
= R
"(
653 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
659 #include "a
/header
.h
"
660 #include "path
/to
/a
/header
.h
"
661 #include "path
/to
/header
.h
"
667 EXPECT_EQ(PostCode
, runCheckOnCode
<EarlyInAlphabetHeaderInserterCheck
>(
668 PreCode
, "workspace_folder/clang_tidy/tests/"
669 "insert_includes_test_header.cc"));
672 TEST(IncludeInserterTest
, HandleOrderInSubdirectory
) {
673 const char *PreCode
= R
"(
674 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
680 #include "path
/to
/a
/header
.h
"
681 #include "path
/to
/header
.h
"
687 const char *PostCode
= R
"(
688 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
694 #include "a
/header
.h
"
695 #include "path
/to
/a
/header
.h
"
696 #include "path
/to
/header
.h
"
702 EXPECT_EQ(PostCode
, runCheckOnCode
<EarlyInAlphabetHeaderInserterCheck
>(
703 PreCode
, "workspace_folder/clang_tidy/tests/"
704 "insert_includes_test_header.cc"));
707 TEST(IncludeInserterTest
, InvalidHeaderName
) {
708 const char *PreCode
= R
"(
709 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
714 #include "path
/to
/a
/header
.h
"
719 const char *PostCode
= R
"(
720 #include "clang_tidy
/tests
/insert_includes_test_header
.h
"
730 #include "path
/to
/a
/header
.h
"
737 runCheckOnCode
<InvalidIncludeInserterCheck
>(
738 PreCode
, "clang_tidy/tests/insert_includes_test_header.cc"));
741 TEST(IncludeInserterTest
, InsertHeaderObjectiveC
) {
742 const char *PreCode
= R
"(
743 #import "clang_tidy
/tests
/insert_includes_test_header
.h
"
748 const char *PostCode
= R
"(
749 #import "clang_tidy
/tests
/insert_includes_test_header
.h
"
759 runCheckOnCode
<ObjCEarlyInAlphabetHeaderInserterCheck
>(
760 PreCode
, "repo/clang_tidy/tests/insert_includes_test_header.mm"));
763 TEST(IncludeInserterTest
, InsertCategoryHeaderObjectiveC
) {
764 const char *PreCode
= R
"(
765 #import "top_level_test_header
.h
"
770 const char *PostCode
= R
"(
771 #import "top_level_test_header
.h
"
772 #import "top_level_test_header
+foo
.h
"
778 EXPECT_EQ(PostCode
, runCheckOnCode
<ObjCCategoryHeaderInserterCheck
>(
779 PreCode
, "top_level_test_header.mm"));
782 TEST(IncludeInserterTest
, InsertGeneratedHeaderObjectiveC
) {
783 const char *PreCode
= R
"(
784 #import "clang_tidy
/tests
/insert_includes_test_header
.h
"
789 #include "path
/to
/a
/header
.h
"
794 const char *PostCode
= R
"(
795 #import "clang_tidy
/tests
/insert_includes_test_header
.h
"
800 #include "path
/to
/a
/header
.h
"
802 #import "clang_tidy
/tests
/generated_file
.proto
.h
"
810 runCheckOnCode
<ObjCGeneratedHeaderInserterCheck
>(
811 PreCode
, "repo/clang_tidy/tests/insert_includes_test_header.mm"));
814 } // anonymous namespace