[clang-tools-extra] 19ab2a6 - [include-cleaner] Show includes matched by refs in HTML report.

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Wed Nov 23 04:41:40 PST 2022


Author: Sam McCall
Date: 2022-11-23T13:41:24+01:00
New Revision: 19ab2a671eb32bede5a0bf686aebf3c6838848e3

URL: https://github.com/llvm/llvm-project/commit/19ab2a671eb32bede5a0bf686aebf3c6838848e3
DIFF: https://github.com/llvm/llvm-project/commit/19ab2a671eb32bede5a0bf686aebf3c6838848e3.diff

LOG: [include-cleaner] Show includes matched by refs in HTML report.

Demo: https://htmlpreview.github.io/?https://gist.githubusercontent.com/sam-mccall/ecee6869e37af3db28089b64d8dce806/raw/8736e64c45af411e2c2d72adaed2dfc4410a5b36/ASTTests.html%25202

Differential Revision: https://reviews.llvm.org/D138219

Added: 
    

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

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/include-cleaner/lib/AnalysisInternal.h b/clang-tools-extra/include-cleaner/lib/AnalysisInternal.h
index ec378dca6ec70..1b85c679f6a7d 100644
--- a/clang-tools-extra/include-cleaner/lib/AnalysisInternal.h
+++ b/clang-tools-extra/include-cleaner/lib/AnalysisInternal.h
@@ -84,7 +84,8 @@ llvm::SmallVector<Header> findHeaders(const SymbolLocation &Loc,
                                       const PragmaIncludes *PI);
 
 /// Write an HTML summary of the analysis to the given stream.
-void writeHTMLReport(FileID File, llvm::ArrayRef<Decl *> Roots,
+void writeHTMLReport(FileID File, const RecordedPP::RecordedIncludes &Includes,
+                     llvm::ArrayRef<Decl *> Roots,
                      llvm::ArrayRef<SymbolReference> MacroRefs, ASTContext &Ctx,
                      PragmaIncludes *PI, llvm::raw_ostream &OS);
 

diff  --git a/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp b/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp
index 2b5d67c299550..477fea6e3fc0e 100644
--- a/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp
+++ b/clang-tools-extra/include-cleaner/lib/HTMLReport.cpp
@@ -51,6 +51,7 @@ constexpr llvm::StringLiteral CSS = R"css(
   #hover p, #hover pre { margin: 0; }
   #hover .target.implicit { background-color: #bbb; }
   #hover .target.ambiguous { background-color: #caf; }
+  .missing, .unused { background-color: #faa !important; }
   #hover th { color: #008; text-align: right; padding-right: 0.5em; }
   #hover .target:not(:first-child) {
     margin-top: 1em;
@@ -110,8 +111,10 @@ class Reporter {
   llvm::raw_ostream &OS;
   const ASTContext &Ctx;
   const SourceManager &SM;
+  const RecordedPP::RecordedIncludes &Includes;
   const PragmaIncludes *PI;
-  FileID File;
+  FileID MainFile;
+  const FileEntry *MainFE;
 
   // References to symbols from the main file.
   // FIXME: should we deduplicate these?
@@ -120,6 +123,8 @@ class Reporter {
     RefType Type;
     SmallVector<SymbolLocation> Locations;
     SmallVector<Header> Headers;
+    SmallVector<const Include *> Includes;
+    bool Satisfied = false; // Is the include present?
   };
   std::vector<Target> Targets;
   // Points within the main file that reference a Target.
@@ -136,7 +141,7 @@ class Reporter {
   std::vector<Ref> Refs;
 
   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.
@@ -151,19 +156,35 @@ class Reporter {
     }
 
     for (const auto &Loc : T.Locations)
-      T.Headers = findHeaders(Loc, SM, PI);
+      T.Headers.append(findHeaders(Loc, SM, PI));
+
+    for (const auto &H : T.Headers) {
+      T.Includes.append(Includes.match(H));
+      // FIXME: library should signal main-file refs somehow.
+      // Non-physical refs to the main-file should be possible.
+      if (H.kind() == Header::Physical && H.physical() == MainFE)
+        T.Satisfied = true;
+    }
+    if (!T.Includes.empty())
+      T.Satisfied = true;
+    // Include pointers are meaningfully ordered as they are backed by a vector.
+    llvm::sort(T.Includes);
+    T.Includes.erase(std::unique(T.Includes.begin(), T.Includes.end()),
+                     T.Includes.end());
 
     return T;
   }
 
 public:
-  Reporter(llvm::raw_ostream &OS, ASTContext &Ctx, const PragmaIncludes *PI,
-           FileID File)
-      : OS(OS), Ctx(Ctx), SM(Ctx.getSourceManager()), PI(PI), File(File) {}
+  Reporter(llvm::raw_ostream &OS, ASTContext &Ctx,
+           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)) {}
 
   void addRef(const SymbolReference &SR) {
     auto [File, Offset] = SM.getDecomposedLoc(SM.getFileLoc(SR.RefLocation));
-    if (File != this->File) {
+    if (File != this->MainFile) {
       // 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! " << SR.Target << " at "
@@ -289,15 +310,23 @@ class Reporter {
       OS << "</td></tr>\n";
     }
 
+    for (const auto *I : T.Includes) {
+      OS << "<tr><th>Included</th><td>";
+      escapeString(I->Spelled);
+      OS << ", <a href='#line" << I->Line << "'>line " << I->Line << "</a>";
+      OS << "</td></tr>";
+    }
+
     OS << "</table>";
   }
 
   void writeCode() {
     llvm::sort(Refs);
-    llvm::StringRef Code = SM.getBufferData(File);
+    llvm::StringRef Code = SM.getBufferData(MainFile);
 
     OS << "<pre onclick='select(event)' class='code'>";
-    OS << "<code class='line'>";
+    OS << "<code class='line' id='line1'>";
+    unsigned LineNum = 1;
     auto Rest = llvm::makeArrayRef(Refs);
     unsigned End = 0;
     for (unsigned I = 0; I < Code.size(); ++I) {
@@ -310,12 +339,14 @@ class Reporter {
       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
-           << "'>◊</span>";
+        OS << "<span class='ref sel implicit"
+           << (Targets[R.TargetIndex].Satisfied ? "" : " missing")
+           << "' data-hover='t" << R.TargetIndex << "'>◊</span>";
         Rest = Rest.drop_front();
       };
       // Accumulate all explicit refs that appear on the same token.
       std::string TargetList;
+      bool Unsatisfied = false;
       Rest = Rest.drop_while([&](const Ref &R) {
         if (R.Offset != I)
           return false;
@@ -323,16 +354,18 @@ class Reporter {
           TargetList.push_back(',');
         TargetList.push_back('t');
         TargetList.append(std::to_string(R.TargetIndex));
+        Unsatisfied = Unsatisfied || !Targets[R.TargetIndex].Satisfied;
         return true;
       });
       if (!TargetList.empty()) {
         assert(End == 0 && "Overlapping tokens!");
-        OS << "<span class='ref sel' data-hover='" << TargetList << "'>";
-        End = I + Lexer::MeasureTokenLength(SM.getComposedLoc(File, I), SM,
+        OS << "<span class='ref sel" << (Unsatisfied ? " missing" : "")
+           << "' data-hover='" << TargetList << "'>";
+        End = I + Lexer::MeasureTokenLength(SM.getComposedLoc(MainFile, I), SM,
                                             Ctx.getLangOpts());
       }
       if (Code[I] == '\n')
-        OS << "</code>\n<code class='line'>";
+        OS << "</code>\n<code class='line' id='line" << (++LineNum) << "'>";
       else
         escapeChar(Code[I]);
     }
@@ -342,10 +375,11 @@ class Reporter {
 
 } // namespace
 
-void writeHTMLReport(FileID File, llvm::ArrayRef<Decl *> Roots,
+void writeHTMLReport(FileID File, const RecordedPP::RecordedIncludes &Includes,
+                     llvm::ArrayRef<Decl *> Roots,
                      llvm::ArrayRef<SymbolReference> MacroRefs, ASTContext &Ctx,
                      PragmaIncludes *PI, llvm::raw_ostream &OS) {
-  Reporter R(OS, Ctx, PI, File);
+  Reporter R(OS, Ctx, Includes, PI, File);
   for (Decl *Root : Roots)
     walkAST(*Root, [&](SourceLocation Loc, const NamedDecl &D, RefType T) {
       R.addRef(SymbolReference{Loc, D, T});

diff  --git a/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp b/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp
index 64ee17e566855..1231576bbfd32 100644
--- a/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp
+++ b/clang-tools-extra/include-cleaner/tool/IncludeCleaner.cpp
@@ -71,8 +71,8 @@ class HTMLReportAction : public clang::ASTFrontendAction {
                    << ": " << EC.message() << "\n";
       exit(1);
     }
-    writeHTMLReport(AST.Ctx->getSourceManager().getMainFileID(), AST.Roots,
-                    PP.MacroReferences, *AST.Ctx, &PI, OS);
+    writeHTMLReport(AST.Ctx->getSourceManager().getMainFileID(), PP.Includes,
+                    AST.Roots, PP.MacroReferences, *AST.Ctx, &PI, OS);
   }
 };
 


        


More information about the cfe-commits mailing list