Skip to content

Commit b0831c3

Browse files
authored
[clang-tidy][misc-include-cleaner]Avoid to insert same include header multiple times (#65431)
`HeaderIncludes` won't update `ExistingIncludes` during inserting. We need to manage it in tidy check. Fixed: #65285
1 parent 97bf104 commit b0831c3

File tree

3 files changed

+51
-8
lines changed

3 files changed

+51
-8
lines changed

clang-tools-extra/clang-tidy/misc/IncludeCleanerCheck.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "llvm/ADT/STLExtras.h"
3434
#include "llvm/ADT/SmallVector.h"
3535
#include "llvm/ADT/StringRef.h"
36+
#include "llvm/ADT/StringSet.h"
3637
#include "llvm/Support/ErrorHandling.h"
3738
#include "llvm/Support/Path.h"
3839
#include "llvm/Support/Regex.h"
@@ -199,6 +200,8 @@ void IncludeCleanerCheck::check(const MatchFinder::MatchResult &Result) {
199200

200201
tooling::HeaderIncludes HeaderIncludes(getCurrentMainFile(), Code,
201202
FileStyle->IncludeStyle);
203+
// Deduplicate insertions when running in bulk fix mode.
204+
llvm::StringSet<> InsertedHeaders{};
202205
for (const auto &Inc : Missing) {
203206
std::string Spelling = include_cleaner::spellHeader(
204207
{Inc.Missing, PP->getHeaderSearchInfo(), MainFile});
@@ -209,14 +212,18 @@ void IncludeCleanerCheck::check(const MatchFinder::MatchResult &Result) {
209212
// main file.
210213
if (auto Replacement =
211214
HeaderIncludes.insert(llvm::StringRef{Spelling}.trim("\"<>"),
212-
Angled, tooling::IncludeDirective::Include))
213-
diag(SM->getSpellingLoc(Inc.SymRef.RefLocation),
214-
"no header providing \"%0\" is directly included")
215-
<< Inc.SymRef.Target.name()
216-
<< FixItHint::CreateInsertion(
217-
SM->getComposedLoc(SM->getMainFileID(),
218-
Replacement->getOffset()),
219-
Replacement->getReplacementText());
215+
Angled, tooling::IncludeDirective::Include)) {
216+
DiagnosticBuilder DB =
217+
diag(SM->getSpellingLoc(Inc.SymRef.RefLocation),
218+
"no header providing \"%0\" is directly included")
219+
<< Inc.SymRef.Target.name();
220+
if (areDiagsSelfContained() ||
221+
InsertedHeaders.insert(Replacement->getReplacementText()).second) {
222+
DB << FixItHint::CreateInsertion(
223+
SM->getComposedLoc(SM->getMainFileID(), Replacement->getOffset()),
224+
Replacement->getReplacementText());
225+
}
226+
}
220227
}
221228
}
222229

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,10 @@ Changes in existing checks
226226
<clang-tidy/checks/misc/include-cleaner>` check by adding option
227227
`DeduplicateFindings` to output one finding per symbol occurrence.
228228

229+
- Improved :doc:`misc-include-cleaner
230+
<clang-tidy/checks/misc/include-cleaner>` check to avoid fixes insert
231+
same include header multiple times.
232+
229233
- Improved :doc:`misc-redundant-expression
230234
<clang-tidy/checks/misc/redundant-expression>` check to ignore
231235
false-positives in unevaluated context (e.g., ``decltype``).

clang-tools-extra/unittests/clang-tidy/IncludeCleanerTest.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,38 @@ int QuxResult = qux();
184184
)"}}));
185185
}
186186

187+
188+
TEST(IncludeCleanerCheckTest, MultipleTimeMissingInclude) {
189+
const char *PreCode = R"(
190+
#include "bar.h"
191+
192+
int BarResult = bar();
193+
int BazResult_0 = baz_0();
194+
int BazResult_1 = baz_1();
195+
)";
196+
const char *PostCode = R"(
197+
#include "bar.h"
198+
#include "baz.h"
199+
200+
int BarResult = bar();
201+
int BazResult_0 = baz_0();
202+
int BazResult_1 = baz_1();
203+
)";
204+
205+
std::vector<ClangTidyError> Errors;
206+
EXPECT_EQ(PostCode,
207+
runCheckOnCode<IncludeCleanerCheck>(
208+
PreCode, &Errors, "file.cpp", std::nullopt, ClangTidyOptions(),
209+
{{"bar.h", R"(#pragma once
210+
#include "baz.h"
211+
int bar();
212+
)"},
213+
{"baz.h", R"(#pragma once
214+
int baz_0();
215+
int baz_1();
216+
)"}}));
217+
}
218+
187219
TEST(IncludeCleanerCheckTest, SystemMissingIncludes) {
188220
const char *PreCode = R"(
189221
#include <vector>

0 commit comments

Comments
 (0)