Skip to content

Commit 4ef77d6

Browse files
authored
[include-cleaner] Attach Header to AnalysisResults for misisng headers (#110272)
Currently callers of analyze can't get detailed information about a missing header, e.g. resolve path. Only way to get at this is to use low level walkUsed funciton, which is way more complicated than just calling analyze. This enables further analysis, e.g. when includes are spelled relative to inner directories, caller can still know their path relative to repository root.
1 parent 64f2bff commit 4ef77d6

File tree

4 files changed

+24
-18
lines changed

4 files changed

+24
-18
lines changed

clang-tools-extra/include-cleaner/include/clang-include-cleaner/Analysis.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "llvm/ADT/SmallVector.h"
2222
#include "llvm/ADT/StringRef.h"
2323
#include <string>
24+
#include <utility>
2425

2526
namespace clang {
2627
class SourceLocation;
@@ -62,7 +63,8 @@ void walkUsed(llvm::ArrayRef<Decl *> ASTRoots,
6263

6364
struct AnalysisResults {
6465
std::vector<const Include *> Unused;
65-
std::vector<std::string> Missing; // Spellings, like "<vector>"
66+
// Spellings, like "<vector>" paired with the Header that generated it.
67+
std::vector<std::pair<std::string, Header>> Missing;
6668
};
6769

6870
/// Determine which headers should be inserted or removed from the main file.

clang-tools-extra/include-cleaner/lib/Analysis.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626
#include "llvm/ADT/STLExtras.h"
2727
#include "llvm/ADT/STLFunctionalExtras.h"
2828
#include "llvm/ADT/SmallVector.h"
29+
#include "llvm/ADT/StringMap.h"
2930
#include "llvm/ADT/StringRef.h"
30-
#include "llvm/ADT/StringSet.h"
3131
#include "llvm/Support/Error.h"
3232
#include "llvm/Support/ErrorHandling.h"
3333
#include <cassert>
@@ -84,7 +84,7 @@ analyze(llvm::ArrayRef<Decl *> ASTRoots,
8484
auto &SM = PP.getSourceManager();
8585
const auto MainFile = *SM.getFileEntryRefForID(SM.getMainFileID());
8686
llvm::DenseSet<const Include *> Used;
87-
llvm::StringSet<> Missing;
87+
llvm::StringMap<Header> Missing;
8888
if (!HeaderFilter)
8989
HeaderFilter = [](llvm::StringRef) { return false; };
9090
OptionalDirectoryEntryRef ResourceDir =
@@ -119,7 +119,7 @@ analyze(llvm::ArrayRef<Decl *> ASTRoots,
119119
Satisfied = true;
120120
}
121121
if (!Satisfied)
122-
Missing.insert(std::move(Spelling));
122+
Missing.try_emplace(std::move(Spelling), Providers.front());
123123
});
124124

125125
AnalysisResults Results;
@@ -144,8 +144,8 @@ analyze(llvm::ArrayRef<Decl *> ASTRoots,
144144
}
145145
Results.Unused.push_back(&I);
146146
}
147-
for (llvm::StringRef S : Missing.keys())
148-
Results.Missing.push_back(S.str());
147+
for (auto &E : Missing)
148+
Results.Missing.emplace_back(E.first().str(), E.second);
149149
llvm::sort(Results.Missing);
150150
return Results;
151151
}
@@ -158,9 +158,9 @@ std::string fixIncludes(const AnalysisResults &Results,
158158
// Encode insertions/deletions in the magic way clang-format understands.
159159
for (const Include *I : Results.Unused)
160160
cantFail(R.add(tooling::Replacement(FileName, UINT_MAX, 1, I->quote())));
161-
for (llvm::StringRef Spelled : Results.Missing)
162-
cantFail(R.add(tooling::Replacement(FileName, UINT_MAX, 0,
163-
("#include " + Spelled).str())));
161+
for (auto &[Spelled, _] : Results.Missing)
162+
cantFail(R.add(
163+
tooling::Replacement(FileName, UINT_MAX, 0, "#include " + Spelled)));
164164
// "cleanup" actually turns the UINT_MAX replacements into concrete edits.
165165
auto Positioned = cantFail(format::cleanupAroundReplacements(Code, R, Style));
166166
return cantFail(tooling::applyAllReplacements(Code, Positioned));

clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ class Action : public clang::ASTFrontendAction {
192192
case PrintStyle::Changes:
193193
for (const Include *I : Results.Unused)
194194
llvm::outs() << "- " << I->quote() << " @Line:" << I->Line << "\n";
195-
for (const auto &I : Results.Missing)
195+
for (const auto &[I, _] : Results.Missing)
196196
llvm::outs() << "+ " << I << "\n";
197197
break;
198198
case PrintStyle::Final:

clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
#include "llvm/ADT/IntrusiveRefCntPtr.h"
2626
#include "llvm/ADT/SmallVector.h"
2727
#include "llvm/ADT/StringRef.h"
28+
#include "llvm/Support/Error.h"
2829
#include "llvm/Support/MemoryBuffer.h"
2930
#include "llvm/Support/ScopedPrinter.h"
3031
#include "llvm/Support/VirtualFileSystem.h"
@@ -39,6 +40,7 @@
3940

4041
namespace clang::include_cleaner {
4142
namespace {
43+
using testing::_;
4244
using testing::AllOf;
4345
using testing::Contains;
4446
using testing::ElementsAre;
@@ -262,10 +264,12 @@ int x = a + c;
262264
auto Results =
263265
analyze(std::vector<Decl *>{Decls.begin(), Decls.end()},
264266
PP.MacroReferences, PP.Includes, &PI, AST.preprocessor());
267+
auto CHeader = llvm::cantFail(
268+
AST.context().getSourceManager().getFileManager().getFileRef("c.h"));
265269

266270
const Include *B = PP.Includes.atLine(3);
267271
ASSERT_EQ(B->Spelled, "b.h");
268-
EXPECT_THAT(Results.Missing, ElementsAre("\"c.h\""));
272+
EXPECT_THAT(Results.Missing, ElementsAre(Pair("\"c.h\"", Header(CHeader))));
269273
EXPECT_THAT(Results.Unused, ElementsAre(B));
270274
}
271275

@@ -370,7 +374,7 @@ TEST_F(AnalyzeTest, SpellingIncludesWithSymlinks) {
370374
auto Results = analyze(DeclsInTU, {}, PP.Includes, &PI, AST.preprocessor());
371375
// Check that we're spelling header using the symlink, and not underlying
372376
// path.
373-
EXPECT_THAT(Results.Missing, testing::ElementsAre("\"inner.h\""));
377+
EXPECT_THAT(Results.Missing, testing::ElementsAre(Pair("\"inner.h\"", _)));
374378
// header.h should be unused.
375379
EXPECT_THAT(Results.Unused, Not(testing::IsEmpty()));
376380

@@ -379,7 +383,7 @@ TEST_F(AnalyzeTest, SpellingIncludesWithSymlinks) {
379383
auto HeaderFilter = [](llvm::StringRef Path) { return Path == "inner.h"; };
380384
Results = analyze(DeclsInTU, {}, PP.Includes, &PI, AST.preprocessor(),
381385
HeaderFilter);
382-
EXPECT_THAT(Results.Missing, testing::ElementsAre("\"inner.h\""));
386+
EXPECT_THAT(Results.Missing, testing::ElementsAre(Pair("\"inner.h\"", _)));
383387
// header.h should be unused.
384388
EXPECT_THAT(Results.Unused, Not(testing::IsEmpty()));
385389
}
@@ -389,7 +393,7 @@ TEST_F(AnalyzeTest, SpellingIncludesWithSymlinks) {
389393
HeaderFilter);
390394
// header.h should be ignored now.
391395
EXPECT_THAT(Results.Unused, Not(testing::IsEmpty()));
392-
EXPECT_THAT(Results.Missing, testing::ElementsAre("\"inner.h\""));
396+
EXPECT_THAT(Results.Missing, testing::ElementsAre(Pair("\"inner.h\"", _)));
393397
}
394398
}
395399

@@ -414,9 +418,9 @@ TEST(FixIncludes, Basic) {
414418
Inc.add(I);
415419

416420
AnalysisResults Results;
417-
Results.Missing.push_back("\"aa.h\"");
418-
Results.Missing.push_back("\"ab.h\"");
419-
Results.Missing.push_back("<e.h>");
421+
Results.Missing.emplace_back("\"aa.h\"", Header(""));
422+
Results.Missing.emplace_back("\"ab.h\"", Header(""));
423+
Results.Missing.emplace_back("<e.h>", Header(""));
420424
Results.Unused.push_back(Inc.atLine(3));
421425
Results.Unused.push_back(Inc.atLine(4));
422426

@@ -429,7 +433,7 @@ R"cpp(#include "d.h"
429433
)cpp");
430434

431435
Results = {};
432-
Results.Missing.push_back("\"d.h\"");
436+
Results.Missing.emplace_back("\"d.h\"", Header(""));
433437
Code = R"cpp(#include "a.h")cpp";
434438
EXPECT_EQ(fixIncludes(Results, "d.cc", Code, format::getLLVMStyle()),
435439
R"cpp(#include "d.h"

0 commit comments

Comments
 (0)