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 @@ -48,7 +48,8 @@ /// Write an HTML summary of the analysis to the given stream. /// FIXME: Once analysis has a public API, this should be public too. -void writeHTMLReport(FileID File, llvm::ArrayRef Roots, ASTContext &Ctx, +void writeHTMLReport(FileID File, llvm::ArrayRef Roots, + llvm::ArrayRef MacroRefs, ASTContext &Ctx, llvm::raw_ostream &OS); } // namespace include_cleaner 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 @@ -15,11 +15,12 @@ #include "AnalysisInternal.h" #include "clang-include-cleaner/Analysis.h" +#include "clang-include-cleaner/Types.h" #include "clang/AST/ASTContext.h" -#include "clang/AST/Decl.h" #include "clang/AST/PrettyPrinter.h" #include "clang/Basic/SourceManager.h" #include "clang/Lex/Lexer.h" +#include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/raw_ostream.h" namespace clang::include_cleaner { @@ -38,6 +39,7 @@ } .ref { text-decoration: underline; color: #008; } .sel { position: relative; cursor: pointer; } + .ref.implicit { background-color: #ff8; } #hover { background-color: #aaccff; border: 1px solid black; z-index: 1; @@ -47,6 +49,8 @@ } #hover p, #hover pre { margin: 0; } #hover section header { font-weight: bold; } + #hover section.implicit { background-color: #bbb; } + #hover section.ambiguous { background-color: #caf; } #hover section:not(:first-child) { margin-top: 1em; } )css"; @@ -75,18 +79,67 @@ )js"; // Print the declaration tersely, but enough to identify e.g. which overload. -std::string printDecl(const NamedDecl &ND) { +std::string printSymbol(const Symbol &Sym) { std::string S; llvm::raw_string_ostream OS(S); - PrintingPolicy PP = ND.getASTContext().getPrintingPolicy(); - PP.FullyQualifiedName = true; - PP.TerseOutput = true; - PP.SuppressInitializers = true; - ND.print(OS, PP); + switch (Sym.kind()) { + case Symbol::Declaration: { + const auto &D = Sym.declaration(); + PrintingPolicy PP = D.getASTContext().getPrintingPolicy(); + PP.FullyQualifiedName = true; + PP.TerseOutput = true; + PP.SuppressInitializers = true; + D.print(OS, PP); + break; + } + case Symbol::Macro: + OS << "#define " << Sym.macro().Name->getName() << " ..."; + break; + case Symbol::Standard: + OS << "(defined in stdlib)"; + break; + } llvm::erase_value(S, '\n'); return S; } +// Categorize the symbol, like FunctionDecl or Macro +llvm::StringRef describeSymbol(const Symbol &Sym) { + switch (Sym.kind()) { + case Symbol::Declaration: + return Sym.declaration().getDeclKindName(); + case Symbol::Macro: + return "Macro"; + case Symbol::Standard: + return "Standard symbol"; + } + llvm_unreachable("unhandled symbol kind"); +} + +// Categorize the symbol, like FunctionDecl or Macro +std::string locateSymbol(const Symbol &Sym, const SourceManager &SM) { + switch (Sym.kind()) { + case Symbol::Declaration: + return Sym.declaration().getLocation().printToString(SM); + case Symbol::Macro: + return Sym.macro().Definition.printToString(SM); + case Symbol::Standard: + return ("stdlib header " + Sym.standard().header().name()).str(); + } + llvm_unreachable("unhandled symbol kind"); +} + +llvm::StringRef refType(RefType T) { + switch (T) { + case RefType::Explicit: + return ""; + case RefType::Implicit: + return "implicit"; + case RefType::Ambiguous: + return "ambiguous"; + } +} + class Reporter { llvm::raw_ostream &OS; const ASTContext &Ctx; @@ -94,29 +147,41 @@ FileID File; // Symbols that are referenced from the main file. + // FIXME: should we deduplicate these? struct Target { - const NamedDecl *D; + Symbol Sym; + RefType Type; + SmallVector
Providers; }; std::vector Targets; // Points within the main file that reference a Target. - std::vector> Refs; + // Implicit refs will be marked with a symbol just before the token. + struct Ref { + unsigned Offset; + bool Implicit; + size_t TargetIndex; + bool operator<(const Ref &Other) const { + return std::forward_as_tuple(Offset, !Implicit, TargetIndex) < + std::forward_as_tuple(Other.Offset, !Other.Implicit, TargetIndex); + } + }; + std::vector Refs; public: Reporter(llvm::raw_ostream &OS, ASTContext &Ctx, FileID File) : OS(OS), Ctx(Ctx), SM(Ctx.getSourceManager()), File(File) {} - void addRef(SourceLocation Loc, const NamedDecl &D) { - auto [File, Offset] = SM.getDecomposedLoc(SM.getFileLoc(Loc)); + void addRef(const SymbolReference &SR, llvm::ArrayRef
Headers) { + auto [File, Offset] = SM.getDecomposedLoc(SM.getFileLoc(SR.RefLocation)); if (File != this->File) { // Can get here e.g. if there's an #include inside a root Decl. // FIXME: do something more useful than this. - llvm::errs() << "Ref location outside file! " - << D.getQualifiedNameAsString() << " at " - << Loc.printToString(SM) << "\n"; + llvm::errs() << "Ref location outside file! " << SR.Target << " at " + << SR.RefLocation.printToString(SM) << "\n"; return; } - Targets.push_back({&D}); - Refs.push_back({Offset, Targets.size() - 1}); + Targets.push_back({SR.Target, SR.RT, {Headers.begin(), Headers.end()}}); + Refs.push_back({Offset, SR.RT == RefType::Implicit, Targets.size() - 1}); } void write() { @@ -126,7 +191,8 @@ OS << "\n"; OS << "\n"; for (unsigned I = 0; I < Targets.size(); ++I) { - OS << "