hokein created this revision.
hokein added a reviewer: kadircet.
Herald added a project: All.
hokein requested review of this revision.
Herald added a project: clang-tools-extra.

Based on https://reviews.llvm.org/D137697


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D137698

Files:
  clang-tools-extra/include-cleaner/include/clang-include-cleaner/Record.h
  clang-tools-extra/include-cleaner/lib/Analysis.cpp
  clang-tools-extra/include-cleaner/lib/CMakeLists.txt
  clang-tools-extra/include-cleaner/lib/Record.cpp
  clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp

Index: clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp
===================================================================
--- clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp
+++ clang-tools-extra/include-cleaner/unittests/AnalysisTest.cpp
@@ -68,6 +68,10 @@
   }
 };
 
+std::string guard(const llvm::StringRef Code) {
+  return "#pragma once\n" + Code.str();
+}
+
 TEST_F(WalkUsedTest, Basic) {
   llvm::Annotations Code(R"cpp(
   #include "header.h"
@@ -78,14 +82,14 @@
   }
   )cpp");
   Inputs.Code = Code.code();
-  Inputs.ExtraFiles["header.h"] = R"cpp(
+  Inputs.ExtraFiles["header.h"] = guard(R"cpp(
   void foo();
   namespace std { class vector {}; }
-  )cpp";
-  Inputs.ExtraFiles["private.h"] = R"cpp(
-    // IWYU pragma: private, include "path/public.h"
-    class Private {};
-  )cpp";
+  )cpp");
+  Inputs.ExtraFiles["private.h"] = guard(R"cpp(
+  // IWYU pragma: private, include "path/public.h"
+  class Private {};
+  )cpp");
   TestAST AST(Inputs);
 
   auto HeaderFile = Header(AST.fileManager().getFile("header.h").get());
@@ -123,21 +127,26 @@
   Inputs.Code = R"cpp(
     #include "header1.h"
     #include "header2.h"
+    #include "header3.h"
   )cpp";
-  Inputs.ExtraFiles["header1.h"] = R"cpp(
-    // IWYU pragma: private, include "path/public.h"
-  )cpp";
-  Inputs.ExtraFiles["header2.h"] = R"cpp(
-    #include "detail1.h" // IWYU pragma: export
+  Inputs.ExtraFiles["header1.h"] = guard(R"cpp(
+  // IWYU pragma: private, include "path/public.h"
+  )cpp");
+  Inputs.ExtraFiles["header2.h"] = guard(R"cpp(
+  #include "detail1.h" // IWYU pragma: export
 
-    // IWYU pragma: begin_exports
-    #include "detail2.h"
-    // IWYU pragma: end_exports
+  // IWYU pragma: begin_exports
+  #include "detail2.h"
+  // IWYU pragma: end_exports
 
-    #include "normal.h"
-  )cpp";
+  #include "normal.h"
+  )cpp");
+  Inputs.ExtraFiles["header3.h"] = guard(R"cpp(
+  #include "imp.inc"
+  )cpp");
   Inputs.ExtraFiles["normal.h"] = Inputs.ExtraFiles["detail1.h"] =
-      Inputs.ExtraFiles["detail2.h"] = "";
+      Inputs.ExtraFiles["detail2.h"] = guard("");
+  Inputs.ExtraFiles["imp.inc"] = "";
   TestAST AST(Inputs);
   const auto &SM = AST.sourceManager();
   auto &FM = SM.getFileManager();
@@ -156,6 +165,9 @@
   EXPECT_THAT(findIncludeHeaders(SourceLocFromFile("detail2.h"), SM, PI),
               UnorderedElementsAre(Header(FM.getFile("header2.h").get()),
                                    Header(FM.getFile("detail2.h").get())));
+  // header3.h is the first #includer for the  non self-contained header imp.inc
+  EXPECT_THAT(findIncludeHeaders(SourceLocFromFile("imp.inc"), SM, PI),
+              UnorderedElementsAre(Header(FM.getFile("header3.h").get())));
 
   EXPECT_THAT(findIncludeHeaders(SourceLocFromFile("normal.h"), SM, PI),
               UnorderedElementsAre(Header(FM.getFile("normal.h").get())));
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
@@ -14,6 +14,7 @@
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Lex/PPCallbacks.h"
 #include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/Inclusions/Header.h"
 
 namespace clang::include_cleaner {
 
@@ -35,12 +36,26 @@
 class PragmaIncludes::RecordPragma : public PPCallbacks, public CommentHandler {
 public:
   RecordPragma(const CompilerInstance &CI, PragmaIncludes *Out)
-      : SM(CI.getSourceManager()), Out(Out) {}
+      : SM(CI.getSourceManager()),
+        HeaderInfo(CI.getPreprocessor().getHeaderSearchInfo()), Out(Out) {}
 
   void FileChanged(SourceLocation Loc, FileChangeReason Reason,
                    SrcMgr::CharacteristicKind FileType,
                    FileID PrevFID) override {
     InMainFile = SM.isWrittenInMainFile(Loc);
+
+    if (Reason == PPCallbacks::ExitFile) {
+      // At file exit time HeaderSearchInfo is valid and can be used to
+      // determine whether the file was a self-contained header or not.
+      if (const FileEntry *FE = SM.getFileEntryForID(PrevFID)) {
+        if (tooling::isSelfContainedHeader(FE, SM, HeaderInfo))
+          Out->NonSelfContainedFiles.erase(
+              SM.getFileEntryForID(PrevFID)->getUniqueID());
+        else
+          Out->NonSelfContainedFiles.insert(
+              SM.getFileEntryForID(PrevFID)->getUniqueID());
+      }
+    }
   }
 
   void EndOfMainFile() override {
@@ -138,6 +153,7 @@
 private:
   bool InMainFile = false;
   const SourceManager &SM;
+  HeaderSearch &HeaderInfo;
   PragmaIncludes *Out;
   // Track the last line "IWYU pragma: keep" was seen in the main file, 1-based.
   int LastPragmaKeepInMainFileLine = -1;
@@ -184,6 +200,10 @@
   return Results;
 }
 
+bool PragmaIncludes::isSelfContained(const FileEntry *ID) const {
+  return !NonSelfContainedFiles.contains(ID->getUniqueID());
+}
+
 std::unique_ptr<ASTConsumer> RecordedAST::record() {
   class Recorder : public ASTConsumer {
     RecordedAST *Out;
Index: clang-tools-extra/include-cleaner/lib/CMakeLists.txt
===================================================================
--- clang-tools-extra/include-cleaner/lib/CMakeLists.txt
+++ clang-tools-extra/include-cleaner/lib/CMakeLists.txt
@@ -11,5 +11,6 @@
   clangBasic
   clangLex
   clangToolingInclusionsStdlib
+  clangToolingInclusions
   )
 
Index: clang-tools-extra/include-cleaner/lib/Analysis.cpp
===================================================================
--- clang-tools-extra/include-cleaner/lib/Analysis.cpp
+++ clang-tools-extra/include-cleaner/lib/Analysis.cpp
@@ -38,14 +38,28 @@
   // FIXME: Handle references of macros.
 }
 
+// Find a responsible header for the given file FID. If the file is non
+// self-contained, we find its first includer that is self-contained by walking
+// up the include stack.
+static const FileEntry *getResponsibleHeader(FileID FID,
+                                             const SourceManager &SM,
+                                             const PragmaIncludes &PI) {
+  const FileEntry *FE = SM.getFileEntryForID(FID);
+  for (; FID != SM.getMainFileID(); FE = SM.getFileEntryForID(FID)) {
+    if (!FE || PI.isSelfContained(FE))
+      return FE;
+
+    FID = SM.getFileID(SM.getIncludeLoc(FID));
+  }
+  return FE;
+}
+
 llvm::SmallVector<Header> findIncludeHeaders(const SymbolLocation &SLoc,
                                              const SourceManager &SM,
                                              const PragmaIncludes &PI) {
   llvm::SmallVector<Header> Results;
   if (auto *Loc = std::get_if<SourceLocation>(&SLoc)) {
-    // FIXME: Handle non self-contained files.
-    FileID FID = SM.getFileID(*Loc);
-    const auto *FE = SM.getFileEntryForID(FID);
+    const auto *FE = getResponsibleHeader(SM.getFileID(*Loc), SM, PI);
     if (!FE)
       return {};
 
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
@@ -62,6 +62,9 @@
   llvm::SmallVector<const FileEntry *>
   getExporters(const FileEntry *File, FileManager &FM) const;
 
+  /// Returns true if the given file is a self-contained file.
+  bool isSelfContained(const FileEntry *File) const;
+
 private:
   class RecordPragma;
   /// 1-based Line numbers for the #include directives of the main file that
@@ -85,8 +88,10 @@
   llvm::DenseMap<llvm::sys::fs::UniqueID, std::string /*RealPath*/>
       RealPathNamesByUID;
 
+  /// Contains all non self-contained files during the parsing.
+  llvm::DenseSet<llvm::sys::fs::UniqueID> NonSelfContainedFiles;
+
   // FIXME: add support for clang use_instead pragma
-  // FIXME: add selfcontained file.
 };
 
 // Contains recorded parser events relevant to 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