diff --git a/clang-tools-extra/include-cleaner/lib/AnalysisInternal.h b/clang-tools-extra/include-cleaner/lib/AnalysisInternal.h --- a/clang-tools-extra/include-cleaner/lib/AnalysisInternal.h +++ b/clang-tools-extra/include-cleaner/lib/AnalysisInternal.h @@ -29,6 +29,7 @@ namespace clang { class ASTContext; class Decl; +class HeaderSearch; class NamedDecl; namespace include_cleaner { @@ -87,7 +88,8 @@ void writeHTMLReport(FileID File, const RecordedPP::RecordedIncludes &Includes, llvm::ArrayRef Roots, llvm::ArrayRef MacroRefs, ASTContext &Ctx, - PragmaIncludes *PI, llvm::raw_ostream &OS); + HeaderSearch &HS, PragmaIncludes *PI, + llvm::raw_ostream &OS); } // namespace include_cleaner } // namespace clang diff --git a/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp b/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp --- a/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp +++ b/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp @@ -18,6 +18,7 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/PrettyPrinter.h" #include "clang/Basic/SourceManager.h" +#include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Lexer.h" #include "clang/Tooling/Inclusions/StandardLibrary.h" #include "llvm/Support/ScopedPrinter.h" @@ -29,7 +30,7 @@ constexpr llvm::StringLiteral CSS = R"css( body { margin: 0; } pre { line-height: 1.5em; counter-reset: line; margin: 0; } - pre .line { counter-increment: line; } + pre .line:not(.added) { counter-increment: line; } pre .line::before { content: counter(line); display: inline-block; @@ -37,6 +38,7 @@ text-align: right; width: 3em; padding-right: 0.5em; margin-right: 0.5em; } + pre .line.added::before { content: '+' } .ref, .inc { text-decoration: underline; color: #008; } .sel { position: relative; cursor: pointer; } .ref.implicit { background-color: #ff8; } @@ -52,6 +54,7 @@ #hover .target.implicit, .provides .implicit { background-color: #bbb; } #hover .target.ambiguous, .provides .ambiguous { background-color: #caf; } .missing, .unused { background-color: #faa !important; } + .inserted { background-color: #bea !important; } .semiused { background-color: #888 !important; } #hover th { color: #008; text-align: right; padding-right: 0.5em; } #hover .target:not(:first-child) { @@ -59,6 +62,8 @@ padding-top: 1em; border-top: 1px solid #444; } + .ref.missing #hover .insert { font-weight: bold; } + .ref:not(.missing) #hover .insert { font-style: italic; } )css"; constexpr llvm::StringLiteral JS = R"js( @@ -128,6 +133,7 @@ llvm::raw_ostream &OS; const ASTContext &Ctx; const SourceManager &SM; + HeaderSearch &HS; const RecordedPP::RecordedIncludes &Includes; const PragmaIncludes *PI; FileID MainFile; @@ -142,6 +148,7 @@ SmallVector
Headers; SmallVector Includes; bool Satisfied = false; // Is the include present? + std::string Insert; // If we had no includes, what would we insert? }; std::vector Targets; // Points within the main file that reference a Target. @@ -157,6 +164,7 @@ }; std::vector Refs; llvm::DenseMap> IncludeRefs; + llvm::StringMap> Insertion; llvm::StringRef includeType(const Include *I) { auto &List = IncludeRefs[I]; @@ -169,8 +177,24 @@ return "semiused"; } + std::string spellHeader(const Header &H) { + switch (H.kind()) { + case Header::Physical: { + bool IsSystem = false; + std::string Path = HS.suggestPathToFileForDiagnostics( + H.physical(), MainFE->tryGetRealPathName(), &IsSystem); + return IsSystem ? "<" + Path + ">" : "\"" + Path + "\""; + } + case Header::Standard: + return H.standard().name().str(); + case Header::Verbatim: + return H.verbatim().str(); + } + llvm_unreachable("Unknown Header kind"); + } + Target makeTarget(const SymbolReference &SR) { - Target T{SR.Target, SR.RT, {}, {}, {}}; + Target T{SR.Target, SR.RT, {}, {}, {}, {}, {}}; // Duplicates logic from walkUsed(), which doesn't expose SymbolLocations. // FIXME: use locateDecl and friends once implemented. @@ -200,16 +224,21 @@ llvm::sort(T.Includes); T.Includes.erase(std::unique(T.Includes.begin(), T.Includes.end()), T.Includes.end()); + + if (!T.Headers.empty()) + // FIXME: library should tell us which header to use. + T.Insert = spellHeader(T.Headers.front()); return T; } public: - Reporter(llvm::raw_ostream &OS, ASTContext &Ctx, + Reporter(llvm::raw_ostream &OS, ASTContext &Ctx, HeaderSearch &HS, const RecordedPP::RecordedIncludes &Includes, const PragmaIncludes *PI, FileID MainFile) - : OS(OS), Ctx(Ctx), SM(Ctx.getSourceManager()), Includes(Includes), - PI(PI), MainFile(MainFile), MainFE(SM.getFileEntryForID(MainFile)) {} + : OS(OS), Ctx(Ctx), SM(Ctx.getSourceManager()), HS(HS), + Includes(Includes), PI(PI), MainFile(MainFile), + MainFE(SM.getFileEntryForID(MainFile)) {} void addRef(const SymbolReference &SR) { auto [File, Offset] = SM.getDecomposedLoc(SM.getFileLoc(SR.RefLocation)); @@ -225,6 +254,9 @@ Targets.push_back(makeTarget(SR)); for (const auto *I : Targets.back().Includes) IncludeRefs[I].push_back(Targets.size() - 1); + if (Targets.back().Type == RefType::Explicit && !Targets.back().Satisfied && + !Targets.back().Insert.empty()) + Insertion[Targets.back().Insert].push_back(Targets.size() - 1); } void write() { @@ -233,6 +265,13 @@ OS << "\n"; OS << "\n"; OS << "\n"; + for (const auto &Ins : Insertion) { + OS << "\n"; + } for (auto &Inc : Includes.all()) { OS << "