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.

Demo: https://gistpreview.github.io/?fec9b77c726cfb3cc7c424b197e3f68c


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D137677

Files:
  clang-tools-extra/include-cleaner/lib/AnalysisInternal.h
  clang-tools-extra/include-cleaner/lib/HTMLReport.cpp
  clang-tools-extra/include-cleaner/lib/Types.cpp
  clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp

Index: clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp
===================================================================
--- clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp
+++ clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp
@@ -47,6 +47,13 @@
 
 class HTMLReportAction : public clang::ASTFrontendAction {
   RecordedAST AST;
+  RecordedPP PP;
+
+  void ExecuteAction() override {
+    auto &P = getCompilerInstance().getPreprocessor();
+    P.addPPCallbacks(PP.record(P));
+    ASTFrontendAction::ExecuteAction();
+  }
 
   std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
                                                  StringRef File) override {
@@ -62,7 +69,7 @@
       exit(1);
     }
     writeHTMLReport(AST.Ctx->getSourceManager().getMainFileID(), AST.Roots,
-                    *AST.Ctx, OS);
+                    PP.MacroReferences, *AST.Ctx, OS);
   }
 };
 
Index: clang-tools-extra/include-cleaner/lib/Types.cpp
===================================================================
--- clang-tools-extra/include-cleaner/lib/Types.cpp
+++ clang-tools-extra/include-cleaner/lib/Types.cpp
@@ -21,7 +21,7 @@
       return OS << ND->getNameAsString();
     return OS << S.declaration().getDeclKindName();
   case Symbol::Macro:
-    return OS << S.macro().Name;
+    return OS << S.macro().Name->getName();
   case Symbol::Standard:
     return OS << S.standard().scope() << S.standard().name();
   }
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
@@ -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<Header> Providers;
   };
   std::vector<Target> Targets;
   // Points within the main file that reference a Target.
-  std::vector<std::pair</*Offset*/ unsigned, /*TargetIndex*/ unsigned>> 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<Ref> 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<Header> 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 << "<style>" << CSS << "</style>\n";
     OS << "<script>" << JS << "</script>\n";
     for (unsigned I = 0; I < Targets.size(); ++I) {
-      OS << "<template id='t" << I << "'><section>";
+      OS << "<template id='t" << I << "'><section class='"
+         << refType(Targets[I].Type) << "'>";
       writeTarget(Targets[I]);
       OS << "</section></template>\n";
     }
@@ -157,15 +223,27 @@
   }
 
   void writeTarget(const Target &T) {
-    OS << "<header>" << T.D->getDeclKindName() << " ";
-    escapeString(T.D->getQualifiedNameAsString());
-    OS << "</header>";
+    OS << "<header>";
+    if (T.Type != RefType::Explicit)
+      OS << refType(T.Type) << " ";
+    OS << describeSymbol(T.Sym) << " ";
+    escapeString(llvm::to_string(T.Sym));
+    OS << "</header>\n";
 
     OS << "<p>declared at ";
-    escapeString(SM.getFileLoc(T.D->getLocation()).printToString(SM));
-    OS << "</p><pre>";
-    escapeString(printDecl(*T.D));
-    OS << "</pre>";
+    escapeString(locateSymbol(T.Sym, SM));
+    OS << "</p>\n";
+
+    OS << "<pre>";
+    escapeString(printSymbol(T.Sym));
+    OS << "</pre>\n";
+
+    OS << "<ul>";
+    for (Header H : T.Providers) {
+      OS << "Candidate: ";
+      escapeString(llvm::to_string(H));
+    }
+    OS << "</ul>\n";
   }
 
   void writeCode() {
@@ -182,14 +260,23 @@
         OS << "</span>";
         End = 0;
       }
+      // Handle implicit refs, which are rendered *before* the token.
+      while (!Rest.empty() && Rest.front().Offset == I &&
+             Rest.front().Implicit) {
+        const Ref &R = Rest.front();
+        OS << "<span class='ref sel implicit' data-hover='t" << R.TargetIndex
+           << "'>&loz;</span>";
+        Rest = Rest.drop_front();
+      };
+      // Accumulate all explicit refs that appear on the same token.
       std::string TargetList;
-      Rest = Rest.drop_while([&](auto &R) {
-        if (R.first != I)
+      Rest = Rest.drop_while([&](const Ref &R) {
+        if (R.Offset != I)
           return false;
         if (!TargetList.empty())
           TargetList.push_back(',');
         TargetList.push_back('t');
-        TargetList.append(std::to_string(R.second));
+        TargetList.append(std::to_string(R.TargetIndex));
         return true;
       });
       if (!TargetList.empty()) {
@@ -209,13 +296,14 @@
 
 } // namespace
 
-void writeHTMLReport(FileID File, llvm::ArrayRef<Decl *> Roots, ASTContext &Ctx,
+void writeHTMLReport(FileID File, llvm::ArrayRef<Decl *> Roots,
+                     llvm::ArrayRef<SymbolReference> MacroRefs, ASTContext &Ctx,
                      llvm::raw_ostream &OS) {
   Reporter R(OS, Ctx, File);
-  for (Decl *Root : Roots)
-    walkAST(*Root, [&](SourceLocation Loc, const NamedDecl &D, RefType) {
-      R.addRef(Loc, D);
-    });
+  walkUsed(Roots, MacroRefs, Ctx.getSourceManager(),
+           [&](const SymbolReference &Ref, llvm::ArrayRef<Header> Headers) {
+             R.addRef(Ref, Headers);
+           });
   R.write();
 }
 
Index: clang-tools-extra/include-cleaner/lib/AnalysisInternal.h
===================================================================
--- clang-tools-extra/include-cleaner/lib/AnalysisInternal.h
+++ 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<Decl *> Roots, ASTContext &Ctx,
+void writeHTMLReport(FileID File, llvm::ArrayRef<Decl *> Roots,
+                     llvm::ArrayRef<SymbolReference> MacroRefs, ASTContext &Ctx,
                      llvm::raw_ostream &OS);
 
 } // namespace include_cleaner
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to