Skip to content

Commit 77feba5

Browse files
authored
[clang-tidy][IncludeCleaner] Fix analysis supression in presence of verbatim spellings (llvm#68185)
1 parent 6d30d94 commit 77feba5

File tree

3 files changed

+30
-16
lines changed

3 files changed

+30
-16
lines changed

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

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "clang/Lex/Preprocessor.h"
3131
#include "clang/Tooling/Core/Replacement.h"
3232
#include "clang/Tooling/Inclusions/HeaderIncludes.h"
33+
#include "clang/Tooling/Inclusions/StandardLibrary.h"
3334
#include "llvm/ADT/DenseSet.h"
3435
#include "llvm/ADT/STLExtras.h"
3536
#include "llvm/ADT/SmallVector.h"
@@ -97,9 +98,12 @@ bool IncludeCleanerCheck::shouldIgnore(const include_cleaner::Header &H) {
9798
return llvm::any_of(IgnoreHeadersRegex, [&H](const llvm::Regex &R) {
9899
switch (H.kind()) {
99100
case include_cleaner::Header::Standard:
101+
// We don't trim angle brackets around standard library headers
102+
// deliberately, so that they are only matched as <vector>, otherwise
103+
// having just `.*/vector` might yield false positives.
100104
return R.match(H.standard().name());
101105
case include_cleaner::Header::Verbatim:
102-
return R.match(H.verbatim());
106+
return R.match(H.verbatim().trim("<>\""));
103107
case include_cleaner::Header::Physical:
104108
return R.match(H.physical().getFileEntry().tryGetRealPathName());
105109
}
@@ -179,12 +183,14 @@ void IncludeCleanerCheck::check(const MatchFinder::MatchResult &Result) {
179183
if (getCurrentMainFile().endswith(PHeader))
180184
continue;
181185
}
182-
183-
if (llvm::none_of(
184-
IgnoreHeadersRegex,
185-
[Resolved = (*I.Resolved).getFileEntry().tryGetRealPathName()](
186-
const llvm::Regex &R) { return R.match(Resolved); }))
187-
Unused.push_back(&I);
186+
auto StdHeader = tooling::stdlib::Header::named(
187+
I.quote(), PP->getLangOpts().CPlusPlus ? tooling::stdlib::Lang::CXX
188+
: tooling::stdlib::Lang::C);
189+
if (StdHeader && shouldIgnore(*StdHeader))
190+
continue;
191+
if (shouldIgnore(*I.Resolved))
192+
continue;
193+
Unused.push_back(&I);
188194
}
189195

190196
llvm::StringRef Code = SM->getBufferData(SM->getMainFileID());

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -249,14 +249,12 @@ Changes in existing checks
249249
- Improved :doc:`misc-const-correctness
250250
<clang-tidy/checks/misc/const-correctness>` check to avoid false positive when
251251
using pointer to member function.
252-
253-
- Improved :doc:`misc-include-cleaner
254-
<clang-tidy/checks/misc/include-cleaner>` check by adding option
255-
`DeduplicateFindings` to output one finding per symbol occurrence.
256252

257253
- Improved :doc:`misc-include-cleaner
258-
<clang-tidy/checks/misc/include-cleaner>` check to avoid fixes insert
259-
same include header multiple times.
254+
<clang-tidy/checks/misc/include-cleaner>` check by adding option
255+
`DeduplicateFindings` to output one finding per symbol occurrence, avoid
256+
inserting the same header multiple times, fix a bug where `IgnoreHeaders`
257+
option won't work with verbatim/std headers.
260258

261259
- Improved :doc:`misc-redundant-expression
262260
<clang-tidy/checks/misc/redundant-expression>` check to ignore

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,18 +59,20 @@ TEST(IncludeCleanerCheckTest, SuppressUnusedIncludes) {
5959
#include "foo/qux.h"
6060
#include "baz/qux/qux.h"
6161
#include <vector>
62+
#include <list>
6263
)";
6364

6465
const char *PostCode = R"(
6566
#include "bar.h"
6667
#include "foo/qux.h"
6768
#include <vector>
69+
#include <list>
6870
)";
6971

7072
std::vector<ClangTidyError> Errors;
7173
ClangTidyOptions Opts;
7274
Opts.CheckOptions["IgnoreHeaders"] = llvm::StringRef{llvm::formatv(
73-
"bar.h;{0};{1};vector",
75+
"bar.h;{0};{1};vector;<list>;",
7476
llvm::Regex::escape(appendPathFileSystemIndependent({"foo", "qux.h"})),
7577
llvm::Regex::escape(appendPathFileSystemIndependent({"baz", "qux"})))};
7678
EXPECT_EQ(
@@ -79,6 +81,7 @@ TEST(IncludeCleanerCheckTest, SuppressUnusedIncludes) {
7981
PreCode, &Errors, "file.cpp", std::nullopt, Opts,
8082
{{"bar.h", "#pragma once"},
8183
{"vector", "#pragma once"},
84+
{"list", "#pragma once"},
8285
{appendPathFileSystemIndependent({"foo", "qux.h"}), "#pragma once"},
8386
{appendPathFileSystemIndependent({"baz", "qux", "qux.h"}),
8487
"#pragma once"}}));
@@ -163,30 +166,37 @@ TEST(IncludeCleanerCheckTest, SuppressMissingIncludes) {
163166
int BarResult = bar();
164167
int BazResult = baz();
165168
int QuxResult = qux();
169+
int PrivResult = test();
170+
std::vector x;
166171
)";
167172

168173
ClangTidyOptions Opts;
169174
Opts.CheckOptions["IgnoreHeaders"] = llvm::StringRef{
170-
"baz.h;" +
175+
"public.h;<vector>;baz.h;" +
171176
llvm::Regex::escape(appendPathFileSystemIndependent({"foo", "qux.h"}))};
172177
std::vector<ClangTidyError> Errors;
173178
EXPECT_EQ(PreCode, runCheckOnCode<IncludeCleanerCheck>(
174179
PreCode, &Errors, "file.cpp", std::nullopt, Opts,
175180
{{"bar.h", R"(#pragma once
176181
#include "baz.h"
177182
#include "foo/qux.h"
183+
#include "private.h"
178184
int bar();
185+
namespace std { struct vector {}; }
179186
)"},
180187
{"baz.h", R"(#pragma once
181188
int baz();
182189
)"},
190+
{"private.h", R"(#pragma once
191+
// IWYU pragma: private, include "public.h"
192+
int test();
193+
)"},
183194
{appendPathFileSystemIndependent({"foo", "qux.h"}),
184195
R"(#pragma once
185196
int qux();
186197
)"}}));
187198
}
188199

189-
190200
TEST(IncludeCleanerCheckTest, MultipleTimeMissingInclude) {
191201
const char *PreCode = R"(
192202
#include "bar.h"

0 commit comments

Comments
 (0)