diff --git a/clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h b/clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h index d6a6a6aa417c59..a569f219939df2 100644 --- a/clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h +++ b/clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h @@ -147,11 +147,15 @@ struct RecordedPP { /// - for a logical file like , we check Spelled llvm::SmallVector match(Header H) const; + /// Finds the include written on the specified line. + const Include *atLine(unsigned OneBasedIndex) const; + private: std::vector All; // Lookup structures for match(), values are index into All. llvm::StringMap> BySpelling; llvm::DenseMap> ByFile; + llvm::DenseMap ByLine; } Includes; }; diff --git a/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp b/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp index 477fea6e3fc0e0..e28a951592c43d 100644 --- a/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp +++ b/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp @@ -37,7 +37,7 @@ constexpr llvm::StringLiteral CSS = R"css( text-align: right; width: 3em; padding-right: 0.5em; margin-right: 0.5em; } - .ref { text-decoration: underline; color: #008; } + .ref, .inc { text-decoration: underline; color: #008; } .sel { position: relative; cursor: pointer; } .ref.implicit { background-color: #ff8; } #hover { @@ -49,9 +49,10 @@ constexpr llvm::StringLiteral CSS = R"css( padding: 0.5em; } #hover p, #hover pre { margin: 0; } - #hover .target.implicit { background-color: #bbb; } - #hover .target.ambiguous { background-color: #caf; } + #hover .target.implicit, .provides .implicit { background-color: #bbb; } + #hover .target.ambiguous, .provides .ambiguous { background-color: #caf; } .missing, .unused { background-color: #faa !important; } + .semiused { background-color: #888 !important; } #hover th { color: #008; text-align: right; padding-right: 0.5em; } #hover .target:not(:first-child) { margin-top: 1em; @@ -95,6 +96,22 @@ llvm::StringRef describeSymbol(const Symbol &Sym) { llvm_unreachable("unhandled symbol kind"); } +// Return detailed symbol description (declaration), if we have any. +std::string printDetails(const Symbol &Sym) { + std::string S; + if (Sym.kind() == Symbol::Declaration) { + // Print the declaration of the symbol, e.g. to disambiguate overloads. + const auto &D = Sym.declaration(); + PrintingPolicy PP = D.getASTContext().getPrintingPolicy(); + PP.FullyQualifiedName = true; + PP.TerseOutput = true; + PP.SuppressInitializers = true; + llvm::raw_string_ostream SS(S); + D.print(SS, PP); + } + return S; +} + llvm::StringRef refType(RefType T) { switch (T) { case RefType::Explicit: @@ -139,6 +156,18 @@ class Reporter { } }; std::vector Refs; + llvm::DenseMap> IncludeRefs; + + llvm::StringRef includeType(const Include *I) { + auto &List = IncludeRefs[I]; + if (List.empty()) + return "unused"; + if (llvm::any_of(List, [&](unsigned I) { + return Targets[Refs[I].TargetIndex].Type == RefType::Explicit; + })) + return "used"; + return "semiused"; + } Target makeTarget(const SymbolReference &SR) { Target T{SR.Target, SR.RT, {}, {}, {}}; @@ -194,6 +223,8 @@ class Reporter { Refs.push_back({Offset, SR.RT == RefType::Implicit, Targets.size()}); Targets.push_back(makeTarget(SR)); + for (const auto *I : Targets.back().Includes) + IncludeRefs[I].push_back(Targets.size() - 1); } void write() { @@ -202,6 +233,11 @@ class Reporter { OS << "\n"; OS << "\n"; OS << "\n"; + for (auto &Inc : Includes.all()) { + OS << "\n"; + } for (unsigned I = 0; I < Targets.size(); ++I) { OS << "