sammccall created this revision. sammccall added a reviewer: hokein. Herald added a project: All. sammccall requested review of this revision. Herald added a project: clang-tools-extra. Herald added a subscriber: cfe-commits.
Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D138649 Files: clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h clang-tools-extra/include-cleaner/lib/HTMLReport.cpp clang-tools-extra/include-cleaner/lib/Record.cpp
Index: clang-tools-extra/include-cleaner/lib/Record.cpp =================================================================== --- clang-tools-extra/include-cleaner/lib/Record.cpp +++ clang-tools-extra/include-cleaner/lib/Record.cpp @@ -337,6 +337,13 @@ BySpellingIt->second.push_back(Index); if (I.Resolved) ByFile[I.Resolved].push_back(Index); + ByLine[I.Line] = Index; +} + +const Include * +RecordedPP::RecordedIncludes::atLine(unsigned OneBasedIndex) const { + auto It = ByLine.find(OneBasedIndex); + return (It == ByLine.end()) ? nullptr : &All[It->second]; } llvm::SmallVector<const Include *> Index: clang-tools-extra/include-cleaner/lib/HTMLReport.cpp =================================================================== --- clang-tools-extra/include-cleaner/lib/HTMLReport.cpp +++ clang-tools-extra/include-cleaner/lib/HTMLReport.cpp @@ -37,7 +37,7 @@ 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 @@ 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_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 @@ } }; std::vector<Ref> Refs; + llvm::DenseMap<const Include *, std::vector<unsigned>> 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 @@ 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 @@ OS << "<head>\n"; OS << "<style>" << CSS << "</style>\n"; OS << "<script>" << JS << "</script>\n"; + for (auto &Inc : Includes.all()) { + OS << "<template id='i" << Inc.Line << "'>"; + writeInclude(Inc); + OS << "</template>\n"; + } for (unsigned I = 0; I < Targets.size(); ++I) { OS << "<template id='t" << I << "'>"; writeTarget(Targets[I]); @@ -260,6 +296,45 @@ OS << ">"; } + void writeInclude(const Include &Inc) { + OS << "<table class='include'>"; + if (Inc.Resolved) { + OS << "<tr><th>Resolved</td><td>"; + escapeString(Inc.Resolved->getName()); + OS << "</td></tr>\n"; + } + // We show one ref for each symbol: first by (RefType != Explicit, Sequence) + llvm::DenseMap<Symbol, /*RefIndex*/ unsigned> FirstRef; + for (unsigned RefIndex : IncludeRefs[&Inc]) { + const Target &T = Targets[Refs[RefIndex].TargetIndex]; + auto I = FirstRef.try_emplace(T.Sym, RefIndex); + if (!I.second && T.Type == RefType::Explicit && + Targets[Refs[I.first->second].TargetIndex].Type != RefType::Explicit) + I.first->second = RefIndex; + } + std::vector<std::pair<Symbol, unsigned>> Sorted = {FirstRef.begin(), + FirstRef.end()}; + llvm::stable_sort(Sorted, llvm::less_second{}); + for (auto &[S, RefIndex] : Sorted) { + auto &T = Targets[Refs[RefIndex].TargetIndex]; + OS << "<tr class='provides'><th>Provides</td><td>"; + std::string Details = printDetails(S); + if (!Details.empty()) { + OS << "<span class='" << refType(T.Type) << "' title='"; + escapeString(Details); + OS << "'>"; + } + escapeString(llvm::to_string(S)); + if (!Details.empty()) + OS << "</span>"; + + unsigned Line = SM.getLineNumber(MainFile, Refs[RefIndex].Offset); + OS << ", <a href='#line" << Line << "'>line " << Line << "</a>"; + OS << "</td></tr>"; + } + OS << "</table>"; + } + void writeTarget(const Target &T) { OS << "<table class='target " << refType(T.Type) << "'>"; @@ -268,19 +343,10 @@ escapeString(llvm::to_string(T.Sym)); OS << "</code></td></tr>\n"; - if (T.Sym.kind() == Symbol::Declaration) { - // Print the declaration of the symbol, e.g. to disambiguate overloads. - const auto &D = T.Sym.declaration(); - PrintingPolicy PP = D.getASTContext().getPrintingPolicy(); - PP.FullyQualifiedName = true; - PP.TerseOutput = true; - PP.SuppressInitializers = true; - std::string S; - llvm::raw_string_ostream SS(S); - D.print(SS, PP); - + std::string Details = printDetails(T.Sym); + if (!Details.empty()) { OS << "<tr><td></td><td><code>"; - escapeString(S); + escapeString(Details); OS << "</code></td></tr>\n"; } @@ -325,10 +391,26 @@ llvm::StringRef Code = SM.getBufferData(MainFile); OS << "<pre onclick='select(event)' class='code'>"; - OS << "<code class='line' id='line1'>"; - unsigned LineNum = 1; + + const Include *Inc = nullptr; + unsigned LineNum = 0; + // Lines are <code>, include lines have an inner <span>. + auto StartLine = [&] { + ++LineNum; + OS << "<code class='line' id='line" << LineNum << "'>"; + if ((Inc = Includes.atLine(LineNum))) + OS << "<span class='inc sel " << includeType(Inc) << "' data-hover='i" + << Inc->Line << "'>"; + }; + auto EndLine = [&] { + if (Inc) + OS << "</span>"; + OS << "</code>\n"; + }; + auto Rest = llvm::makeArrayRef(Refs); unsigned End = 0; + StartLine(); for (unsigned I = 0; I < Code.size(); ++I) { // Finish refs early at EOL to avoid dealing with splitting the span. if (End && (End == I || Code[I] == '\n')) { @@ -364,12 +446,14 @@ End = I + Lexer::MeasureTokenLength(SM.getComposedLoc(MainFile, I), SM, Ctx.getLangOpts()); } - if (Code[I] == '\n') - OS << "</code>\n<code class='line' id='line" << (++LineNum) << "'>"; - else + if (Code[I] == '\n') { + EndLine(); + StartLine(); + } else escapeChar(Code[I]); } - OS << "</code></pre>\n"; + EndLine(); + OS << "</pre>\n"; } }; Index: clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h =================================================================== --- clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h +++ clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h @@ -147,11 +147,15 @@ /// - for a logical file like <vector>, we check Spelled llvm::SmallVector<const Include *> match(Header H) const; + /// Finds the include written on the specified line. + const Include *atLine(unsigned OneBasedIndex) const; + private: std::vector<Include> All; // Lookup structures for match(), values are index into All. llvm::StringMap<llvm::SmallVector<unsigned>> BySpelling; llvm::DenseMap<const FileEntry *, llvm::SmallVector<unsigned>> ByFile; + llvm::DenseMap<unsigned, unsigned> ByLine; } Includes; };
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits