[clang-tidy] Add UnusedIncludes/MissingIncludes options to misc-include-cleaner (#140600)
These mimick the same options from clangd and allow using the check to only check for unused includes or missing includes.
This commit is contained in:
parent
32946ddd2e
commit
c46a394df9
@ -59,7 +59,9 @@ IncludeCleanerCheck::IncludeCleanerCheck(StringRef Name,
|
||||
: ClangTidyCheck(Name, Context),
|
||||
IgnoreHeaders(
|
||||
utils::options::parseStringList(Options.get("IgnoreHeaders", ""))),
|
||||
DeduplicateFindings(Options.get("DeduplicateFindings", true)) {
|
||||
DeduplicateFindings(Options.get("DeduplicateFindings", true)),
|
||||
UnusedIncludes(Options.get("UnusedIncludes", true)),
|
||||
MissingIncludes(Options.get("MissingIncludes", true)) {
|
||||
for (const auto &Header : IgnoreHeaders) {
|
||||
if (!llvm::Regex{Header}.isValid())
|
||||
configurationDiag("Invalid ignore headers regex '%0'") << Header;
|
||||
@ -68,12 +70,19 @@ IncludeCleanerCheck::IncludeCleanerCheck(StringRef Name,
|
||||
HeaderSuffix += "$";
|
||||
IgnoreHeadersRegex.emplace_back(HeaderSuffix);
|
||||
}
|
||||
|
||||
if (UnusedIncludes == false && MissingIncludes == false)
|
||||
this->configurationDiag("The check 'misc-include-cleaner' will not "
|
||||
"perform any analysis because 'UnusedIncludes' and "
|
||||
"'MissingIncludes' are both false.");
|
||||
}
|
||||
|
||||
void IncludeCleanerCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
|
||||
Options.store(Opts, "IgnoreHeaders",
|
||||
utils::options::serializeStringList(IgnoreHeaders));
|
||||
Options.store(Opts, "DeduplicateFindings", DeduplicateFindings);
|
||||
Options.store(Opts, "UnusedIncludes", UnusedIncludes);
|
||||
Options.store(Opts, "MissingIncludes", MissingIncludes);
|
||||
}
|
||||
|
||||
bool IncludeCleanerCheck::isLanguageVersionSupported(
|
||||
@ -200,39 +209,43 @@ void IncludeCleanerCheck::check(const MatchFinder::MatchResult &Result) {
|
||||
if (!FileStyle)
|
||||
FileStyle = format::getLLVMStyle();
|
||||
|
||||
for (const auto *Inc : Unused) {
|
||||
diag(Inc->HashLocation, "included header %0 is not used directly")
|
||||
<< llvm::sys::path::filename(Inc->Spelled,
|
||||
llvm::sys::path::Style::posix)
|
||||
<< FixItHint::CreateRemoval(CharSourceRange::getCharRange(
|
||||
SM->translateLineCol(SM->getMainFileID(), Inc->Line, 1),
|
||||
SM->translateLineCol(SM->getMainFileID(), Inc->Line + 1, 1)));
|
||||
if (UnusedIncludes) {
|
||||
for (const auto *Inc : Unused) {
|
||||
diag(Inc->HashLocation, "included header %0 is not used directly")
|
||||
<< llvm::sys::path::filename(Inc->Spelled,
|
||||
llvm::sys::path::Style::posix)
|
||||
<< FixItHint::CreateRemoval(CharSourceRange::getCharRange(
|
||||
SM->translateLineCol(SM->getMainFileID(), Inc->Line, 1),
|
||||
SM->translateLineCol(SM->getMainFileID(), Inc->Line + 1, 1)));
|
||||
}
|
||||
}
|
||||
|
||||
tooling::HeaderIncludes HeaderIncludes(getCurrentMainFile(), Code,
|
||||
FileStyle->IncludeStyle);
|
||||
// Deduplicate insertions when running in bulk fix mode.
|
||||
llvm::StringSet<> InsertedHeaders{};
|
||||
for (const auto &Inc : Missing) {
|
||||
std::string Spelling = include_cleaner::spellHeader(
|
||||
{Inc.Missing, PP->getHeaderSearchInfo(), MainFile});
|
||||
bool Angled = llvm::StringRef{Spelling}.starts_with("<");
|
||||
// We might suggest insertion of an existing include in edge cases, e.g.,
|
||||
// include is present in a PP-disabled region, or spelling of the header
|
||||
// turns out to be the same as one of the unresolved includes in the
|
||||
// main file.
|
||||
if (auto Replacement =
|
||||
HeaderIncludes.insert(llvm::StringRef{Spelling}.trim("\"<>"),
|
||||
Angled, tooling::IncludeDirective::Include)) {
|
||||
DiagnosticBuilder DB =
|
||||
diag(SM->getSpellingLoc(Inc.SymRef.RefLocation),
|
||||
"no header providing \"%0\" is directly included")
|
||||
<< Inc.SymRef.Target.name();
|
||||
if (areDiagsSelfContained() ||
|
||||
InsertedHeaders.insert(Replacement->getReplacementText()).second) {
|
||||
DB << FixItHint::CreateInsertion(
|
||||
SM->getComposedLoc(SM->getMainFileID(), Replacement->getOffset()),
|
||||
Replacement->getReplacementText());
|
||||
if (MissingIncludes) {
|
||||
tooling::HeaderIncludes HeaderIncludes(getCurrentMainFile(), Code,
|
||||
FileStyle->IncludeStyle);
|
||||
// Deduplicate insertions when running in bulk fix mode.
|
||||
llvm::StringSet<> InsertedHeaders{};
|
||||
for (const auto &Inc : Missing) {
|
||||
std::string Spelling = include_cleaner::spellHeader(
|
||||
{Inc.Missing, PP->getHeaderSearchInfo(), MainFile});
|
||||
bool Angled = llvm::StringRef{Spelling}.starts_with("<");
|
||||
// We might suggest insertion of an existing include in edge cases, e.g.,
|
||||
// include is present in a PP-disabled region, or spelling of the header
|
||||
// turns out to be the same as one of the unresolved includes in the
|
||||
// main file.
|
||||
if (auto Replacement = HeaderIncludes.insert(
|
||||
llvm::StringRef{Spelling}.trim("\"<>"), Angled,
|
||||
tooling::IncludeDirective::Include)) {
|
||||
DiagnosticBuilder DB =
|
||||
diag(SM->getSpellingLoc(Inc.SymRef.RefLocation),
|
||||
"no header providing \"%0\" is directly included")
|
||||
<< Inc.SymRef.Target.name();
|
||||
if (areDiagsSelfContained() ||
|
||||
InsertedHeaders.insert(Replacement->getReplacementText()).second) {
|
||||
DB << FixItHint::CreateInsertion(
|
||||
SM->getComposedLoc(SM->getMainFileID(), Replacement->getOffset()),
|
||||
Replacement->getReplacementText());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,6 +47,10 @@ private:
|
||||
std::vector<StringRef> IgnoreHeaders;
|
||||
// Whether emit only one finding per usage of a symbol.
|
||||
const bool DeduplicateFindings;
|
||||
// Whether to report unused includes.
|
||||
const bool UnusedIncludes;
|
||||
// Whether to report missing includes.
|
||||
const bool MissingIncludes;
|
||||
llvm::SmallVector<llvm::Regex> IgnoreHeadersRegex;
|
||||
bool shouldIgnore(const include_cleaner::Header &H);
|
||||
};
|
||||
|
@ -184,6 +184,11 @@ Changes in existing checks
|
||||
`AnalyzePointers` option and fixing false positives when using const array
|
||||
type.
|
||||
|
||||
- Improved :doc:`misc-include-cleaner
|
||||
<clang-tidy/checks/misc/include-cleaner>` check by adding the options
|
||||
`UnusedIncludes` and `MissingIncludes`, which specify whether the check should
|
||||
report unused or missing includes respectively.
|
||||
|
||||
- Improved :doc:`misc-redundant-expression
|
||||
<clang-tidy/checks/misc/redundant-expression>` check by providing additional
|
||||
examples and fixing some macro related false positives.
|
||||
@ -208,7 +213,7 @@ Changes in existing checks
|
||||
diagnosing designated initializers for ``std::array`` initializations.
|
||||
|
||||
- Improved :doc:`modernize-use-ranges
|
||||
<clang-tidy/checks/modernize/use-ranges>` check by updating suppress
|
||||
<clang-tidy/checks/modernize/use-ranges>` check by updating suppress
|
||||
warnings logic for ``nullptr`` in ``std::find``.
|
||||
|
||||
- Improved :doc:`modernize-use-starts-ends-with
|
||||
|
@ -38,3 +38,14 @@ Options
|
||||
|
||||
A boolean that controls whether the check should deduplicate findings for the
|
||||
same symbol. Defaults to `true`.
|
||||
|
||||
.. option:: UnusedIncludes
|
||||
|
||||
A boolean that controls whether the check should report unused includes
|
||||
(includes that are not used directly). Defaults to `true`.
|
||||
|
||||
.. option:: MissingIncludes
|
||||
|
||||
A boolean that controls whether the check should report missing includes
|
||||
(header files from which symbols are used but which are not directly included).
|
||||
Defaults to `true`.
|
||||
|
@ -0,0 +1,10 @@
|
||||
// RUN: %check_clang_tidy %s misc-include-cleaner %t \
|
||||
// RUN: -config='{CheckOptions: \
|
||||
// RUN: {"misc-include-cleaner.UnusedIncludes": false,\
|
||||
// RUN: "misc-include-cleaner.MissingIncludes": false,\
|
||||
// RUN: }}' -- -I%S/Inputs -isystem%S/Inputs/system -fno-delayed-template-parsing
|
||||
|
||||
// CHECK-MESSAGES: warning: The check 'misc-include-cleaner' will not perform any analysis because 'UnusedIncludes' and 'MissingIncludes' are both false. [clang-tidy-config]
|
||||
|
||||
#include "bar.h"
|
||||
// CHECK-FIXES-NOT: {{^}}#include "baz.h"{{$}}
|
@ -316,6 +316,64 @@ DECLARE {
|
||||
)"}}));
|
||||
}
|
||||
|
||||
TEST(IncludeCleanerCheckTest, UnusedIncludes) {
|
||||
const char *PreCode = R"(
|
||||
#include "bar.h")";
|
||||
|
||||
{
|
||||
std::vector<ClangTidyError> Errors;
|
||||
runCheckOnCode<IncludeCleanerCheck>(PreCode, &Errors, "file.cpp", {},
|
||||
ClangTidyOptions(),
|
||||
{{"bar.h", "#pragma once"}});
|
||||
ASSERT_THAT(Errors.size(), testing::Eq(1U));
|
||||
EXPECT_EQ(Errors.front().Message.Message,
|
||||
"included header bar.h is not used directly");
|
||||
}
|
||||
{
|
||||
std::vector<ClangTidyError> Errors;
|
||||
ClangTidyOptions Opts;
|
||||
Opts.CheckOptions["test-check-0.UnusedIncludes"] = "false";
|
||||
runCheckOnCode<IncludeCleanerCheck>(PreCode, &Errors, "file.cpp", {}, Opts,
|
||||
{{"bar.h", "#pragma once"}});
|
||||
ASSERT_THAT(Errors.size(), testing::Eq(0U));
|
||||
}
|
||||
}
|
||||
|
||||
TEST(IncludeCleanerCheckTest, MissingIncludes) {
|
||||
const char *PreCode = R"(
|
||||
#include "baz.h" // IWYU pragma: keep
|
||||
|
||||
int BarResult1 = bar();)";
|
||||
|
||||
{
|
||||
std::vector<ClangTidyError> Errors;
|
||||
runCheckOnCode<IncludeCleanerCheck>(PreCode, &Errors, "file.cpp", {},
|
||||
ClangTidyOptions(),
|
||||
{{"baz.h", R"(#pragma once
|
||||
#include "bar.h"
|
||||
)"},
|
||||
{"bar.h", R"(#pragma once
|
||||
int bar();
|
||||
)"}});
|
||||
ASSERT_THAT(Errors.size(), testing::Eq(1U));
|
||||
EXPECT_EQ(Errors.front().Message.Message,
|
||||
"no header providing \"bar\" is directly included");
|
||||
}
|
||||
{
|
||||
std::vector<ClangTidyError> Errors;
|
||||
ClangTidyOptions Opts;
|
||||
Opts.CheckOptions["test-check-0.MissingIncludes"] = "false";
|
||||
runCheckOnCode<IncludeCleanerCheck>(PreCode, &Errors, "file.cpp", {}, Opts,
|
||||
{{"baz.h", R"(#pragma once
|
||||
#include "bar.h"
|
||||
)"},
|
||||
{"bar.h", R"(#pragma once
|
||||
int bar();
|
||||
)"}});
|
||||
ASSERT_THAT(Errors.size(), testing::Eq(0U));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace tidy
|
||||
|
Loading…
x
Reference in New Issue
Block a user