kadircet created this revision.
kadircet added reviewers: hokein, sammccall.
Herald added a project: All.
kadircet requested review of this revision.
Herald added a project: clang-tools-extra.
Herald added a subscriber: cfe-commits.

Depens on D135953 <https://reviews.llvm.org/D135953>


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D138200

Files:
  clang-tools-extra/include-cleaner/lib/Analysis.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
@@ -25,11 +25,49 @@
 
 namespace clang::include_cleaner {
 namespace {
+using testing::Contains;
 using testing::Pair;
 using testing::UnorderedElementsAre;
 
-TEST(WalkUsed, Basic) {
-  // FIXME: Have a fixture for setting up tests.
+class WalkUsedTest : public testing::Test {
+protected:
+  TestInputs Inputs;
+  PragmaIncludes PI;
+  WalkUsedTest() {
+    Inputs.MakeAction = [this] {
+      struct Hook : public SyntaxOnlyAction {
+      public:
+        Hook(PragmaIncludes *Out) : Out(Out) {}
+        bool BeginSourceFileAction(clang::CompilerInstance &CI) override {
+          Out->record(CI);
+          return true;
+        }
+
+        PragmaIncludes *Out;
+      };
+      return std::make_unique<Hook>(&PI);
+    };
+  }
+
+  llvm::DenseMap<size_t, std::vector<Header>>
+  offsetToProviders(TestAST &AST, SourceManager &SM) {
+    llvm::SmallVector<Decl *> TopLevelDecls;
+    for (Decl *D : AST.context().getTranslationUnitDecl()->decls()) {
+      TopLevelDecls.emplace_back(D);
+    }
+    llvm::DenseMap<size_t, std::vector<Header>> OffsetToProviders;
+    walkUsed(TopLevelDecls, /*MacroRefs=*/{}, PI, SM,
+             [&](const SymbolReference &Ref, llvm::ArrayRef<Header> Providers) {
+               auto [FID, Offset] = SM.getDecomposedLoc(Ref.RefLocation);
+               if (FID != SM.getMainFileID())
+                 ADD_FAILURE() << "Reference outside of the main file!";
+               OffsetToProviders.try_emplace(Offset, Providers.vec());
+             });
+    return OffsetToProviders;
+  }
+};
+
+TEST_F(WalkUsedTest, Basic) {
   llvm::Annotations Code(R"cpp(
   #include "header.h"
   #include "private.h"
@@ -39,7 +77,7 @@
     std::$vector^vector $vconstructor^v;
   }
   )cpp");
-  TestInputs Inputs(Code.code());
+  Inputs.Code = Code.code();
   Inputs.ExtraFiles["header.h"] = R"cpp(
   void foo();
   namespace std { class vector {}; }
@@ -49,40 +87,13 @@
     class Private {};
   )cpp";
 
-  PragmaIncludes PI;
-  Inputs.MakeAction = [&PI] {
-    struct Hook : public SyntaxOnlyAction {
-    public:
-      Hook(PragmaIncludes *Out) : Out(Out) {}
-      bool BeginSourceFileAction(clang::CompilerInstance &CI) override {
-        Out->record(CI);
-        return true;
-      }
-
-      PragmaIncludes *Out;
-    };
-    return std::make_unique<Hook>(&PI);
-  };
   TestAST AST(Inputs);
-
-  llvm::SmallVector<Decl *> TopLevelDecls;
-  for (Decl *D : AST.context().getTranslationUnitDecl()->decls()) {
-    TopLevelDecls.emplace_back(D);
-  }
-
   auto &SM = AST.sourceManager();
-  llvm::DenseMap<size_t, std::vector<Header>> OffsetToProviders;
-  walkUsed(TopLevelDecls, /*MacroRefs=*/{}, PI, SM,
-           [&](const SymbolReference &Ref, llvm::ArrayRef<Header> Providers) {
-             auto [FID, Offset] = SM.getDecomposedLoc(Ref.RefLocation);
-             EXPECT_EQ(FID, SM.getMainFileID());
-             OffsetToProviders.try_emplace(Offset, Providers.vec());
-           });
   auto HeaderFile = Header(AST.fileManager().getFile("header.h").get());
   auto MainFile = Header(SM.getFileEntryForID(SM.getMainFileID()));
   auto VectorSTL = Header(tooling::stdlib::Header::named("<vector>").value());
   EXPECT_THAT(
-      OffsetToProviders,
+      offsetToProviders(AST, SM),
       UnorderedElementsAre(
           Pair(Code.point("bar"), UnorderedElementsAre(MainFile)),
           Pair(Code.point("private"),
@@ -92,6 +103,35 @@
           Pair(Code.point("vconstructor"), UnorderedElementsAre(VectorSTL))));
 }
 
+TEST_F(WalkUsedTest, MultipleProviders) {
+  llvm::Annotations Code(R"cpp(
+  #include "header1.h"
+  #include "header2.h"
+  void foo();
+
+  void bar() {
+    $foo^foo();
+  }
+  )cpp");
+  Inputs.Code = Code.code();
+  Inputs.ExtraFiles["header1.h"] = R"cpp(
+  void foo();
+  )cpp";
+  Inputs.ExtraFiles["header2.h"] = R"cpp(
+  void foo();
+  )cpp";
+
+  TestAST AST(Inputs);
+  auto &SM = AST.sourceManager();
+  auto HeaderFile1 = Header(AST.fileManager().getFile("header1.h").get());
+  auto HeaderFile2 = Header(AST.fileManager().getFile("header2.h").get());
+  auto MainFile = Header(SM.getFileEntryForID(SM.getMainFileID()));
+  EXPECT_THAT(
+      offsetToProviders(AST, SM),
+      Contains(Pair(Code.point("foo"),
+                    UnorderedElementsAre(HeaderFile1, HeaderFile2, MainFile))));
+}
+
 TEST(WalkUsed, MacroRefs) {
   llvm::Annotations Hdr(R"cpp(
     #define ^ANSWER 42
@@ -129,6 +169,5 @@
       UnorderedElementsAre(Pair(Main.point(), UnorderedElementsAre(HdrFile))));
 }
 
-
 } // namespace
 } // namespace clang::include_cleaner
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
@@ -8,37 +8,53 @@
 
 #include "clang-include-cleaner/Analysis.h"
 #include "AnalysisInternal.h"
+#include "clang-include-cleaner/Record.h"
 #include "clang-include-cleaner/Types.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Tooling/Inclusions/StandardLibrary.h"
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/SmallVector.h"
+#include <unordered_map>
 
 namespace clang::include_cleaner {
 
+namespace {
+// Gets all the providers for a symbol by tarversing each location.
+llvm::SmallVector<Header> findAllHeaders(const Symbol &S,
+                                         const SourceManager &SM,
+                                         const PragmaIncludes &PI) {
+  llvm::SmallVector<Header> Headers;
+  for (auto &Loc : locateSymbol(S)) {
+    // FIXME: Propagate header hints.
+    Headers.append(findHeaders(Loc.first, SM, PI));
+  }
+  return Headers;
+}
+} // namespace
+
 void walkUsed(llvm::ArrayRef<Decl *> ASTRoots,
               llvm::ArrayRef<SymbolReference> MacroRefs,
               const PragmaIncludes &PI, const SourceManager &SM,
               UsedSymbolCB CB) {
+  // Cache for decl to header mappings, as the same decl might be referenced in
+  // multiple locations and finding providers for each location is expensive.
+  std::unordered_map<NamedDecl *, llvm::SmallVector<Header>> DeclToHeaders;
   tooling::stdlib::Recognizer Recognizer;
   for (auto *Root : ASTRoots) {
     auto &SM = Root->getASTContext().getSourceManager();
     walkAST(*Root, [&](SourceLocation Loc, NamedDecl &ND, RefType RT) {
       SymbolReference SymRef{Loc, ND, RT};
-      if (auto SS = Recognizer(&ND)) {
-        // FIXME: Also report forward decls from main-file, so that the caller
-        // can decide to insert/ignore a header.
-        return CB(SymRef, findHeaders(*SS, SM, PI));
-      }
-      // FIXME: Extract locations from redecls.
-      return CB(SymRef, findHeaders(ND.getLocation(), SM, PI));
+      auto Inserted = DeclToHeaders.try_emplace(&ND);
+      if (Inserted.second)
+        Inserted.first->second = findAllHeaders(ND, SM, PI);
+      return CB(SymRef, Inserted.first->second);
     });
   }
   for (const SymbolReference &MacroRef : MacroRefs) {
     assert(MacroRef.Target.kind() == Symbol::Macro);
-    return CB(MacroRef,
-              findHeaders(MacroRef.Target.macro().Definition, SM, PI));
+    return CB(MacroRef, findAllHeaders(MacroRef.Target, SM, PI));
   }
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to