sammccall updated this revision to Diff 89360.
sammccall added a comment.

Changes based on offline discussion:

- mutable SignalInfo data split into SignalInfo::Signals
- yaml serialized data is SignalInfo::WithSignals, which is currently a 
pair<SignalInfo, Signals>
- FindAllSymbols/FindAllMacros now report in batches, with symbols already 
deduplicated. This makes it easy to count each (file,symbol) only once.
- implemented macro usages because I had to touch that anyway


https://reviews.llvm.org/D30210

Files:
  include-fixer/InMemorySymbolIndex.cpp
  include-fixer/InMemorySymbolIndex.h
  include-fixer/IncludeFixer.cpp
  include-fixer/SymbolIndex.h
  include-fixer/SymbolIndexManager.cpp
  include-fixer/YamlSymbolIndex.cpp
  include-fixer/YamlSymbolIndex.h
  include-fixer/find-all-symbols/FindAllMacros.cpp
  include-fixer/find-all-symbols/FindAllMacros.h
  include-fixer/find-all-symbols/FindAllSymbols.cpp
  include-fixer/find-all-symbols/FindAllSymbols.h
  include-fixer/find-all-symbols/SymbolInfo.cpp
  include-fixer/find-all-symbols/SymbolInfo.h
  include-fixer/find-all-symbols/SymbolReporter.h
  include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp
  include-fixer/tool/ClangIncludeFixer.cpp
  test/include-fixer/Inputs/fake_yaml_db.yaml
  test/include-fixer/Inputs/merge/a.yaml
  test/include-fixer/Inputs/merge/b.yaml
  test/include-fixer/merge.test
  unittests/include-fixer/IncludeFixerTest.cpp
  unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp

Index: unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp
===================================================================
--- unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp
+++ unittests/include-fixer/find-all-symbols/FindAllSymbolsTests.cpp
@@ -19,6 +19,7 @@
 #include "clang/Frontend/PCHContainerOperations.h"
 #include "clang/Tooling/Tooling.h"
 #include "llvm/ADT/IntrusiveRefCntPtr.h"
+#include "llvm/ADT/STLExtras.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/MemoryBuffer.h"
 #include "gtest/gtest.h"
@@ -35,30 +36,36 @@
 public:
   ~TestSymbolReporter() override {}
 
-  void reportSymbol(llvm::StringRef FileName,
-                    const SymbolInfo &Symbol) override {
-    Symbols.push_back(Symbol);
+  void reportSymbols(llvm::StringRef FileName,
+                     SymbolInfo::SignalMap NewSymbols) override {
+    for (const auto &Entry : NewSymbols) Symbols[Entry.first] += Entry.second;
   }
 
   bool hasSymbol(const SymbolInfo &Symbol) const {
-    for (const auto &S : Symbols) {
-      if (S == Symbol)
-        return true;
-    }
-    return false;
+    auto it = Symbols.find(Symbol);
+    return it != Symbols.end() && it->second.Seen > 0;
+  }
+
+  bool hasUse(const SymbolInfo &Symbol) const {
+    auto it = Symbols.find(Symbol);
+    return it != Symbols.end() && it->second.Used > 0;
   }
 
 private:
-  std::vector<SymbolInfo> Symbols;
+  SymbolInfo::SignalMap Symbols;
 };
 
 class FindAllSymbolsTest : public ::testing::Test {
 public:
   bool hasSymbol(const SymbolInfo &Symbol) {
     return Reporter.hasSymbol(Symbol);
   }
 
-  bool runFindAllSymbols(StringRef Code) {
+  bool hasUse(const SymbolInfo &Symbol) {
+    return Reporter.hasUse(Symbol);
+  }
+
+  bool runFindAllSymbols(StringRef HeaderCode, StringRef MainCode) {
     llvm::IntrusiveRefCntPtr<vfs::InMemoryFileSystem> InMemoryFileSystem(
         new vfs::InMemoryFileSystem);
     llvm::IntrusiveRefCntPtr<FileManager> Files(
@@ -98,7 +105,7 @@
         std::make_shared<PCHContainerOperations>());
 
     InMemoryFileSystem->addFile(HeaderName, 0,
-                                llvm::MemoryBuffer::getMemBuffer(Code));
+                                llvm::MemoryBuffer::getMemBuffer(HeaderCode));
 
     std::string Content = "#include\"" + std::string(HeaderName) +
                           "\"\n"
@@ -118,6 +125,7 @@
     SymbolInfo DirtySymbol("ExtraInternal", SymbolInfo::SymbolKind::Class,
                            CleanHeader, 2, {});
 #endif // _MSC_VER && __MINGW32__
+    Content += "\n" + MainCode.str();
     InMemoryFileSystem->addFile(FileName, 0,
                                 llvm::MemoryBuffer::getMemBuffer(Content));
     Invocation.run();
@@ -135,49 +143,64 @@
 };
 
 TEST_F(FindAllSymbolsTest, VariableSymbols) {
-  static const char Code[] = R"(
+  static const char Header[] = R"(
       extern int xargc;
       namespace na {
       static bool SSSS = false;
       namespace nb { const long long *XXXX; }
       })";
-  runFindAllSymbols(Code);
+  static const char Main[] = R"(
+      auto y = &na::nb::XXXX;
+      int main() { if (na::SSSS) return xargc; }
+  )";
+  runFindAllSymbols(Header, Main);
 
   SymbolInfo Symbol =
       SymbolInfo("xargc", SymbolInfo::SymbolKind::Variable, HeaderName, 2, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol = SymbolInfo("SSSS", SymbolInfo::SymbolKind::Variable, HeaderName, 4,
                       {{SymbolInfo::ContextType::Namespace, "na"}});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol = SymbolInfo("XXXX", SymbolInfo::SymbolKind::Variable, HeaderName, 5,
                       {{SymbolInfo::ContextType::Namespace, "nb"},
                        {SymbolInfo::ContextType::Namespace, "na"}});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 }
 
 TEST_F(FindAllSymbolsTest, ExternCSymbols) {
-  static const char Code[] = R"(
+  static const char Header[] = R"(
       extern "C" {
       int C_Func() { return 0; }
       struct C_struct {
         int Member;
       };
       })";
-  runFindAllSymbols(Code);
+  static const char Main[] = R"(
+      C_struct q() {
+        int(*ptr)() = C_Func;
+        return {0};
+      }
+  )";
+  runFindAllSymbols(Header, Main);
 
   SymbolInfo Symbol =
       SymbolInfo("C_Func", SymbolInfo::SymbolKind::Function, HeaderName, 3, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol =
       SymbolInfo("C_struct", SymbolInfo::SymbolKind::Class, HeaderName, 4, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 }
 
 TEST_F(FindAllSymbolsTest, CXXRecordSymbols) {
-  static const char Code[] = R"(
+  static const char Header[] = R"(
       struct Glob {};
       struct A; // Not a defintion, ignored.
       class NOP; // Not a defintion, ignored
@@ -190,26 +213,33 @@
       };
       };  //
       )";
-  runFindAllSymbols(Code);
+  static const char Main[] = R"(
+      static Glob glob;
+      static na::A::AAAA* a;
+  )";
+  runFindAllSymbols(Header, Main);
 
   SymbolInfo Symbol =
       SymbolInfo("Glob", SymbolInfo::SymbolKind::Class, HeaderName, 2, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol = SymbolInfo("A", SymbolInfo::SymbolKind::Class, HeaderName, 6,
                       {{SymbolInfo::ContextType::Namespace, "na"}});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol = SymbolInfo("AAA", SymbolInfo::SymbolKind::Class, HeaderName, 7,
                       {{SymbolInfo::ContextType::Record, "A"},
                        {SymbolInfo::ContextType::Namespace, "na"}});
   EXPECT_FALSE(hasSymbol(Symbol));
+  EXPECT_FALSE(hasUse(Symbol));
 }
 
 TEST_F(FindAllSymbolsTest, CXXRecordSymbolsTemplate) {
-  static const char Code[] = R"(
+  static const char Header[] = R"(
       template <typename T>
-      class T_TEMP {
+      struct T_TEMP {
         template <typename _Tp1>
         struct rebind { typedef T_TEMP<_Tp1> other; };
       };
@@ -222,11 +252,15 @@
       // Ignore specialization.
       template <> class Observer<int> {};
       )";
-  runFindAllSymbols(Code);
+  static const char Main[] = R"(
+      extern T_TEMP<int>::rebind<char> weirdo;
+  )";
+  runFindAllSymbols(Header, Main);
 
   SymbolInfo Symbol =
       SymbolInfo("T_TEMP", SymbolInfo::SymbolKind::Class, HeaderName, 3, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 }
 
 TEST_F(FindAllSymbolsTest, DontIgnoreTemplatePartialSpecialization) {
@@ -239,7 +273,7 @@
       template<class T> void f() {};
       template<> void f<int>() {};
       )";
-  runFindAllSymbols(Code);
+  runFindAllSymbols(Code, "");
   SymbolInfo Symbol =
       SymbolInfo("Class", SymbolInfo::SymbolKind::Class, HeaderName, 4, {});
   EXPECT_TRUE(hasSymbol(Symbol));
@@ -252,7 +286,7 @@
 }
 
 TEST_F(FindAllSymbolsTest, FunctionSymbols) {
-  static const char Code[] = R"(
+  static const char Header[] = R"(
       namespace na {
       int gg(int);
       int f(const int &a) { int Local; static int StaticLocal; return 0; }
@@ -265,91 +299,127 @@
       } // namespace nb
       } // namespace na";
       )";
-  runFindAllSymbols(Code);
+  static const char Main[] = R"(
+      int(*gg)(int) = &na::gg;
+      int main() {
+        (void)na::SSSFFF;
+        na::nb::fun(0);
+        return na::f(gg(0));
+      }
+  )";
+  runFindAllSymbols(Header, Main);
 
   SymbolInfo Symbol =
       SymbolInfo("gg", SymbolInfo::SymbolKind::Function, HeaderName, 3,
                  {{SymbolInfo::ContextType::Namespace, "na"}});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol = SymbolInfo("f", SymbolInfo::SymbolKind::Function, HeaderName, 4,
                       {{SymbolInfo::ContextType::Namespace, "na"}});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol = SymbolInfo("SSSFFF", SymbolInfo::SymbolKind::Function, HeaderName, 5,
                       {{SymbolInfo::ContextType::Namespace, "na"}});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol = SymbolInfo("fun", SymbolInfo::SymbolKind::Function, HeaderName, 10,
                       {{SymbolInfo::ContextType::Namespace, "nb"},
                        {SymbolInfo::ContextType::Namespace, "na"}});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 }
 
 TEST_F(FindAllSymbolsTest, NamespaceTest) {
-  static const char Code[] = R"(
+  static const char Header[] = R"(
       int X1;
       namespace { int X2; }
       namespace { namespace { int X3; } }
       namespace { namespace nb { int X4; } }
       namespace na { inline namespace __1 { int X5; } }
       )";
-  runFindAllSymbols(Code);
+  static const char Main[] = R"(
+      using namespace nb;
+      int main() {
+        X1 = X2;
+        X3 = X4;
+        (void)na::X5;
+      }
+  )";
+  runFindAllSymbols(Header, Main);
 
   SymbolInfo Symbol =
       SymbolInfo("X1", SymbolInfo::SymbolKind::Variable, HeaderName, 2, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol = SymbolInfo("X2", SymbolInfo::SymbolKind::Variable, HeaderName, 3,
                       {{SymbolInfo::ContextType::Namespace, ""}});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol = SymbolInfo("X3", SymbolInfo::SymbolKind::Variable, HeaderName, 4,
                       {{SymbolInfo::ContextType::Namespace, ""},
                        {SymbolInfo::ContextType::Namespace, ""}});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol = SymbolInfo("X4", SymbolInfo::SymbolKind::Variable, HeaderName, 5,
                       {{SymbolInfo::ContextType::Namespace, "nb"},
                        {SymbolInfo::ContextType::Namespace, ""}});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol = SymbolInfo("X5", SymbolInfo::SymbolKind::Variable, HeaderName, 6,
                       {{SymbolInfo::ContextType::Namespace, "na"}});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 }
 
 TEST_F(FindAllSymbolsTest, DecayedTypeTest) {
-  static const char Code[] = "void DecayedFunc(int x[], int y[10]) {}";
-  runFindAllSymbols(Code);
+  static const char Header[] = "void DecayedFunc(int x[], int y[10]) {}";
+  static const char Main[] = R"(int main() { DecayedFunc(nullptr, nullptr); })";
+  runFindAllSymbols(Header, Main);
   SymbolInfo Symbol = SymbolInfo(
       "DecayedFunc", SymbolInfo::SymbolKind::Function, HeaderName, 1, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 }
 
 TEST_F(FindAllSymbolsTest, CTypedefTest) {
-  static const char Code[] = R"(
+  static const char Header[] = R"(
       typedef unsigned size_t_;
       typedef struct { int x; } X;
       using XX = X;
       )";
-  runFindAllSymbols(Code);
+  static const char Main[] = R"(
+      size_t_ f;
+      template<typename T> struct vector{};
+      vector<X> list;
+      void foo(const XX&){}
+  )";
+  runFindAllSymbols(Header, Main);
 
   SymbolInfo Symbol = SymbolInfo("size_t_", SymbolInfo::SymbolKind::TypedefName,
                                  HeaderName, 2, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol =
       SymbolInfo("X", SymbolInfo::SymbolKind::TypedefName, HeaderName, 3, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol =
       SymbolInfo("XX", SymbolInfo::SymbolKind::TypedefName, HeaderName, 4, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 }
 
 TEST_F(FindAllSymbolsTest, EnumTest) {
-  static const char Code[] = R"(
+  static const char Header[] = R"(
       enum Glob_E { G1, G2 };
       enum class Altitude { high='h', low='l'};
       enum { A1, A2 };
@@ -359,42 +429,58 @@
       };
       enum DECL : int;
       )";
-  runFindAllSymbols(Code);
+  static const char Main[] = R"(
+      static auto flags = G1 | G2;
+      static auto alt = Altitude::high;
+      static auto nested = A::X1;
+      extern DECL whatever;
+      static auto flags2 = A1 | A2;
+  )";
+  runFindAllSymbols(Header, Main);
 
   SymbolInfo Symbol =
       SymbolInfo("Glob_E", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 2, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_FALSE(hasUse(Symbol));
 
   Symbol =
       SymbolInfo("G1", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName, 2,
                  {{SymbolInfo::ContextType::EnumDecl, "Glob_E"}});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol =
       SymbolInfo("G2", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName, 2,
                  {{SymbolInfo::ContextType::EnumDecl, "Glob_E"}});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol = SymbolInfo("Altitude", SymbolInfo::SymbolKind::EnumDecl, HeaderName,
                       3, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
   Symbol =
       SymbolInfo("high", SymbolInfo::SymbolKind::EnumConstantDecl, HeaderName,
                  3, {{SymbolInfo::ContextType::EnumDecl, "Altitude"}});
   EXPECT_FALSE(hasSymbol(Symbol));
+  EXPECT_FALSE(hasUse(Symbol));
 
   Symbol = SymbolInfo("A1", SymbolInfo::SymbolKind::EnumConstantDecl,
                       HeaderName, 4, {{SymbolInfo::ContextType::EnumDecl, ""}});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
   Symbol = SymbolInfo("A2", SymbolInfo::SymbolKind::EnumConstantDecl,
                       HeaderName, 4, {{SymbolInfo::ContextType::EnumDecl, ""}});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
   Symbol = SymbolInfo("", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 4, {});
   EXPECT_FALSE(hasSymbol(Symbol));
+  EXPECT_FALSE(hasUse(Symbol));
 
   Symbol = SymbolInfo("A_ENUM", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 7,
                       {{SymbolInfo::ContextType::Record, "A"}});
   EXPECT_FALSE(hasSymbol(Symbol));
+  EXPECT_FALSE(hasUse(Symbol));
 
   Symbol = SymbolInfo("X1", SymbolInfo::SymbolKind::EnumDecl, HeaderName, 7,
                       {{SymbolInfo::ContextType::EnumDecl, "A_ENUM"},
@@ -407,63 +493,83 @@
 }
 
 TEST_F(FindAllSymbolsTest, IWYUPrivatePragmaTest) {
-  static const char Code[] = R"(
+  static const char Header[] = R"(
     // IWYU pragma: private, include "bar.h"
     struct Bar {
     };
   )";
-  runFindAllSymbols(Code);
+  static const char Main[] = R"(
+    Bar bar;
+  )";
+  runFindAllSymbols(Header, Main);
 
   SymbolInfo Symbol =
       SymbolInfo("Bar", SymbolInfo::SymbolKind::Class, "bar.h", 3, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 }
 
 TEST_F(FindAllSymbolsTest, MacroTest) {
-  static const char Code[] = R"(
+  static const char Header[] = R"(
     #define X
     #define Y 1
     #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
   )";
-  runFindAllSymbols(Code);
+  static const char Main[] = R"(
+    #ifdef X
+    int main() { return MAX(0,Y); }
+    #endif
+  )";
+  runFindAllSymbols(Header, Main);
   SymbolInfo Symbol =
       SymbolInfo("X", SymbolInfo::SymbolKind::Macro, HeaderName, 2, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol = SymbolInfo("Y", SymbolInfo::SymbolKind::Macro, HeaderName, 3, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol = SymbolInfo("MAX", SymbolInfo::SymbolKind::Macro, HeaderName, 4, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 }
 
 TEST_F(FindAllSymbolsTest, MacroTestWithIWYU) {
-  static const char Code[] = R"(
+  static const char Header[] = R"(
     // IWYU pragma: private, include "bar.h"
-    #define X
+    #define X 1
     #define Y 1
     #define MAX(X, Y) ((X) > (Y) ? (X) : (Y))
   )";
-  runFindAllSymbols(Code);
+  static const char Main[] = R"(
+    #ifdef X
+    int main() { return MAX(0,Y); }
+    #endif
+  )";
+  runFindAllSymbols(Header, Main);
   SymbolInfo Symbol =
       SymbolInfo("X", SymbolInfo::SymbolKind::Macro, "bar.h", 3, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol = SymbolInfo("Y", SymbolInfo::SymbolKind::Macro, "bar.h", 4, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 
   Symbol = SymbolInfo("MAX", SymbolInfo::SymbolKind::Macro, "bar.h", 5, {});
   EXPECT_TRUE(hasSymbol(Symbol));
+  EXPECT_TRUE(hasUse(Symbol));
 }
 
 TEST_F(FindAllSymbolsTest, NoFriendTest) {
-  static const char Code[] = R"(
+  static const char Header[] = R"(
     class WorstFriend {
       friend void Friend();
       friend class BestFriend;
     };
   )";
-  runFindAllSymbols(Code);
+  runFindAllSymbols(Header, "");
   SymbolInfo Symbol = SymbolInfo("WorstFriend", SymbolInfo::SymbolKind::Class,
                                  HeaderName, 2, {});
   EXPECT_TRUE(hasSymbol(Symbol));
Index: unittests/include-fixer/IncludeFixerTest.cpp
===================================================================
--- unittests/include-fixer/IncludeFixerTest.cpp
+++ unittests/include-fixer/IncludeFixerTest.cpp
@@ -52,37 +52,45 @@
 static std::string runIncludeFixer(
     StringRef Code,
     const std::vector<std::string> &ExtraArgs = std::vector<std::string>()) {
-  std::vector<SymbolInfo> Symbols = {
-      SymbolInfo("string", SymbolInfo::SymbolKind::Class, "<string>", 1,
-                 {{SymbolInfo::ContextType::Namespace, "std"}}),
-      SymbolInfo("sting", SymbolInfo::SymbolKind::Class, "\"sting\"", 1,
-                 {{SymbolInfo::ContextType::Namespace, "std"}}),
-      SymbolInfo("foo", SymbolInfo::SymbolKind::Class, "\"dir/otherdir/qux.h\"",
-                 1, {{SymbolInfo::ContextType::Namespace, "b"},
-                     {SymbolInfo::ContextType::Namespace, "a"}}),
-      SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar.h\"", 1,
-                 {{SymbolInfo::ContextType::Namespace, "b"},
-                  {SymbolInfo::ContextType::Namespace, "a"}}),
-      SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar2.h\"", 1,
-                 {{SymbolInfo::ContextType::Namespace, "c"},
-                  {SymbolInfo::ContextType::Namespace, "a"}}),
-      SymbolInfo("Green", SymbolInfo::SymbolKind::Class, "\"color.h\"", 1,
-                 {{SymbolInfo::ContextType::EnumDecl, "Color"},
-                  {SymbolInfo::ContextType::Namespace, "b"},
-                  {SymbolInfo::ContextType::Namespace, "a"}}),
-      SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"", 1,
-                 {{SymbolInfo::ContextType::Namespace, "__a"},
-                  {SymbolInfo::ContextType::Namespace, "a"}},
-                 /*num_occurrences=*/2),
-      SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"", 2,
-                 {{SymbolInfo::ContextType::Namespace, "a"}},
-                 /*num_occurrences=*/1),
-      SymbolInfo("StrCat", SymbolInfo::SymbolKind::Class, "\"strcat.h\"",
-                 1, {{SymbolInfo::ContextType::Namespace, "str"}}),
-      SymbolInfo("str", SymbolInfo::SymbolKind::Class, "\"str.h\"",
-                 1, {}),
-      SymbolInfo("foo2", SymbolInfo::SymbolKind::Class, "\"foo2.h\"",
-                 1, {}),
+  std::vector<SymbolInfo::WithSignals> Symbols = {
+      {SymbolInfo("string", SymbolInfo::SymbolKind::Class, "<string>", 1,
+                  {{SymbolInfo::ContextType::Namespace, "std"}}),
+       SymbolInfo::Signals{}},
+      {SymbolInfo("sting", SymbolInfo::SymbolKind::Class, "\"sting\"", 1,
+                  {{SymbolInfo::ContextType::Namespace, "std"}}),
+       SymbolInfo::Signals{}},
+      {SymbolInfo("foo", SymbolInfo::SymbolKind::Class,
+                  "\"dir/otherdir/qux.h\"", 1,
+                  {{SymbolInfo::ContextType::Namespace, "b"},
+                   {SymbolInfo::ContextType::Namespace, "a"}}),
+       SymbolInfo::Signals{}},
+      {SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar.h\"", 1,
+                  {{SymbolInfo::ContextType::Namespace, "b"},
+                   {SymbolInfo::ContextType::Namespace, "a"}}),
+       SymbolInfo::Signals{}},
+      {SymbolInfo("bar", SymbolInfo::SymbolKind::Class, "\"bar2.h\"", 1,
+                  {{SymbolInfo::ContextType::Namespace, "c"},
+                   {SymbolInfo::ContextType::Namespace, "a"}}),
+       SymbolInfo::Signals{}},
+      {SymbolInfo("Green", SymbolInfo::SymbolKind::Class, "\"color.h\"", 1,
+                  {{SymbolInfo::ContextType::EnumDecl, "Color"},
+                   {SymbolInfo::ContextType::Namespace, "b"},
+                   {SymbolInfo::ContextType::Namespace, "a"}}),
+       SymbolInfo::Signals{}},
+      {SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"", 1,
+                  {{SymbolInfo::ContextType::Namespace, "__a"},
+                   {SymbolInfo::ContextType::Namespace, "a"}}),
+       SymbolInfo::Signals{/*Seen=*/2, 0}},
+      {SymbolInfo("Vector", SymbolInfo::SymbolKind::Class, "\"Vector.h\"", 2,
+                  {{SymbolInfo::ContextType::Namespace, "a"}}),
+       SymbolInfo::Signals{/*Seen=*/2, 0}},
+      {SymbolInfo("StrCat", SymbolInfo::SymbolKind::Class, "\"strcat.h\"", 1,
+                  {{SymbolInfo::ContextType::Namespace, "str"}}),
+       SymbolInfo::Signals{}},
+      {SymbolInfo("str", SymbolInfo::SymbolKind::Class, "\"str.h\"", 1, {}),
+       SymbolInfo::Signals{}},
+      {SymbolInfo("foo2", SymbolInfo::SymbolKind::Class, "\"foo2.h\"", 1, {}),
+       SymbolInfo::Signals{}},
   };
   auto SymbolIndexMgr = llvm::make_unique<include_fixer::SymbolIndexManager>();
   SymbolIndexMgr->addSymbolIndex([=]() {
Index: test/include-fixer/merge.test
===================================================================
--- test/include-fixer/merge.test
+++ test/include-fixer/merge.test
@@ -9,7 +9,8 @@
 FilePath:        ../include/bar.h
 LineNumber:      1
 Type:            Class
-NumOccurrences:  1
+Seen:            1
+Used:            1
 ...
 ---
 Name:            bar
@@ -19,7 +20,8 @@
 FilePath:        ../include/barbar.h
 LineNumber:      1
 Type:            Class
-NumOccurrences:  1
+Seen:            1
+Used:            0
 ...
 ---
 Name:            foo
@@ -29,5 +31,6 @@
 FilePath:        foo.h
 LineNumber:      1
 Type:            Class
-NumOccurrences:  2
+Seen:            2
+Used:            2
 ...
Index: test/include-fixer/Inputs/merge/b.yaml
===================================================================
--- test/include-fixer/Inputs/merge/b.yaml
+++ test/include-fixer/Inputs/merge/b.yaml
@@ -6,7 +6,8 @@
 FilePath:        foo.h
 LineNumber:      1
 Type:            Class
-NumOccurrences:  1
+Seen:            1
+Used:            2
 ...
 ---
 Name:           bar
@@ -16,5 +17,6 @@
 FilePath:        ../include/barbar.h
 LineNumber:      1
 Type:            Class
-NumOccurrences:  1
+Seen:            1
+Used:            0
 ...
Index: test/include-fixer/Inputs/merge/a.yaml
===================================================================
--- test/include-fixer/Inputs/merge/a.yaml
+++ test/include-fixer/Inputs/merge/a.yaml
@@ -6,7 +6,8 @@
 FilePath:        foo.h
 LineNumber:      1
 Type:            Class
-NumOccurrences:  1
+Seen:            1
+Used:            1
 ...
 ---
 Name:           bar
@@ -16,5 +17,6 @@
 FilePath:        ../include/bar.h
 LineNumber:      1
 Type:            Class
-NumOccurrences:  1
+Seen:            1
+Used:            2
 ...
Index: test/include-fixer/Inputs/fake_yaml_db.yaml
===================================================================
--- test/include-fixer/Inputs/fake_yaml_db.yaml
+++ test/include-fixer/Inputs/fake_yaml_db.yaml
@@ -8,7 +8,8 @@
 FilePath:        foo.h
 LineNumber:      1
 Type:            Class
-NumOccurrences:  1
+Seen:            1
+Used:            0
 ---
 Name:           bar
 Contexts:
@@ -19,7 +20,8 @@
 FilePath:        ../include/bar.h
 LineNumber:      1
 Type:            Class
-NumOccurrences:  1
+Seen:            1
+Used:            0
 ---
 Name:           bar
 Contexts:
@@ -30,7 +32,8 @@
 FilePath:        ../include/bar.h
 LineNumber:      2
 Type:            Class
-NumOccurrences:  3
+Seen:            3
+Used:            0
 ---
 Name:           bar
 Contexts:
@@ -41,20 +44,23 @@
 FilePath:        ../include/zbar.h
 LineNumber:      1
 Type:            Class
-NumOccurrences:  3
+Seen:            3
+Used:            0
 ---
 Name:           b
 Contexts:
 FilePath:        var.h
 LineNumber:      1
 Type:            Variable
-NumOccurrences:  1
+Seen:            1
+Used:            0
 ---
 Name:            bar
 Contexts:
   - ContextType:    Namespace
     ContextName:    c
 FilePath:        test/include-fixer/baz.h
 LineNumber:      1
 Type:            Class
-NumOccurrences:  1
+Seen:            1
+Used:            0
Index: include-fixer/tool/ClangIncludeFixer.cpp
===================================================================
--- include-fixer/tool/ClangIncludeFixer.cpp
+++ include-fixer/tool/ClangIncludeFixer.cpp
@@ -158,6 +158,8 @@
 
 std::unique_ptr<include_fixer::SymbolIndexManager>
 createSymbolIndexManager(StringRef FilePath) {
+  using find_all_symbols::SymbolInfo;
+
   auto SymbolIndexMgr = llvm::make_unique<include_fixer::SymbolIndexManager>();
   switch (DatabaseFormat) {
   case fixed: {
@@ -167,17 +169,19 @@
     std::map<std::string, std::vector<std::string>> SymbolsMap;
     SmallVector<StringRef, 4> SemicolonSplits;
     StringRef(Input).split(SemicolonSplits, ";");
-    std::vector<find_all_symbols::SymbolInfo> Symbols;
+    std::vector<SymbolInfo::WithSignals> Symbols;
     for (StringRef Pair : SemicolonSplits) {
       auto Split = Pair.split('=');
       std::vector<std::string> Headers;
       SmallVector<StringRef, 4> CommaSplits;
       Split.second.split(CommaSplits, ",");
       for (size_t I = 0, E = CommaSplits.size(); I != E; ++I)
-        Symbols.push_back(find_all_symbols::SymbolInfo(
-            Split.first.trim(),
-            find_all_symbols::SymbolInfo::SymbolKind::Unknown,
-            CommaSplits[I].trim(), 1, {}, /*NumOccurrences=*/E - I));
+        Symbols.push_back(
+            {SymbolInfo(Split.first.trim(), SymbolInfo::SymbolKind::Unknown,
+                        CommaSplits[I].trim(), 1, {}),
+             // Use fake "seen" signal for tests, so first header wins.
+             SymbolInfo::Signals(/*Seen=*/static_cast<unsigned>(E - I),
+                                 /*Used=*/0)});
     }
     SymbolIndexMgr->addSymbolIndex([=]() {
       return llvm::make_unique<include_fixer::InMemorySymbolIndex>(Symbols);
Index: include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp
===================================================================
--- include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp
+++ include-fixer/find-all-symbols/tool/FindAllSymbolsMain.cpp
@@ -64,36 +64,28 @@
 
 class YamlReporter : public clang::find_all_symbols::SymbolReporter {
 public:
-  ~YamlReporter() override {
-    for (const auto &Symbol : Symbols) {
-      int FD;
-      SmallString<128> ResultPath;
-      llvm::sys::fs::createUniqueFile(
-          OutputDir + "/" + llvm::sys::path::filename(Symbol.first) +
-              "-%%%%%%.yaml",
-          FD, ResultPath);
-      llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
-      WriteSymbolInfosToStream(OS, Symbol.second);
-    }
-  }
-
-  void reportSymbol(StringRef FileName, const SymbolInfo &Symbol) override {
-    Symbols[FileName].insert(Symbol);
+  void reportSymbols(StringRef FileName,
+                     SymbolInfo::SignalMap Symbols) override {
+    int FD;
+    SmallString<128> ResultPath;
+    llvm::sys::fs::createUniqueFile(
+        OutputDir + "/" + llvm::sys::path::filename(FileName) + "-%%%%%%.yaml",
+        FD, ResultPath);
+    llvm::raw_fd_ostream OS(FD, /*shouldClose=*/true);
+    WriteSymbolInfosToStream(OS, Symbols);
   }
-
-private:
-  std::map<std::string, std::set<SymbolInfo>> Symbols;
 };
 
 bool Merge(llvm::StringRef MergeDir, llvm::StringRef OutputFile) {
   std::error_code EC;
-  std::map<SymbolInfo, int> SymbolToNumOccurrences;
+  SymbolInfo::SignalMap Symbols;
   std::mutex SymbolMutex;
-  auto AddSymbols = [&](ArrayRef<SymbolInfo> Symbols) {
+  auto AddSymbols = [&](ArrayRef<SymbolInfo::WithSignals> NewSymbols) {
     // Synchronize set accesses.
     std::unique_lock<std::mutex> LockGuard(SymbolMutex);
-    for (const auto &Symbol : Symbols)
-      ++SymbolToNumOccurrences[Symbol];
+    for (const auto &Symbol : NewSymbols) {
+      Symbols[Symbol.first] += Symbol.second;
+    }
   };
 
   // Load all symbol files in MergeDir.
@@ -109,8 +101,13 @@
               llvm::errs() << "Can't open " << Path << "\n";
               return;
             }
-            std::vector<SymbolInfo> Symbols =
+            std::vector<SymbolInfo::WithSignals> Symbols =
                 ReadSymbolInfosFromYAML(Buffer.get()->getBuffer());
+            for (auto& Symbol : Symbols) {
+              // Only count one occurrence per file, to avoid spam.
+              Symbol.second.Seen = std::min(Symbol.second.Seen, 1u);
+              Symbol.second.Used = std::min(Symbol.second.Used, 1u);
+            }
             // FIXME: Merge without creating such a heavy contention point.
             AddSymbols(Symbols);
           },
@@ -124,14 +121,7 @@
                  << '\n';
     return false;
   }
-  std::set<SymbolInfo> Result;
-  for (const auto &Entry : SymbolToNumOccurrences) {
-    const auto &Symbol = Entry.first;
-    Result.insert(SymbolInfo(Symbol.getName(), Symbol.getSymbolKind(),
-                             Symbol.getFilePath(), Symbol.getLineNumber(),
-                             Symbol.getContexts(), Entry.second));
-  }
-  WriteSymbolInfosToStream(OS, Result);
+  WriteSymbolInfosToStream(OS, Symbols);
   return true;
 }
 
Index: include-fixer/find-all-symbols/SymbolReporter.h
===================================================================
--- include-fixer/find-all-symbols/SymbolReporter.h
+++ include-fixer/find-all-symbols/SymbolReporter.h
@@ -20,8 +20,8 @@
 public:
   virtual ~SymbolReporter() = default;
 
-  virtual void reportSymbol(llvm::StringRef FileName,
-                            const SymbolInfo &Symbol) = 0;
+  virtual void reportSymbols(llvm::StringRef FileName,
+                             SymbolInfo::SignalMap Symbols) = 0;
 };
 
 } // namespace find_all_symbols
Index: include-fixer/find-all-symbols/SymbolInfo.h
===================================================================
--- include-fixer/find-all-symbols/SymbolInfo.h
+++ include-fixer/find-all-symbols/SymbolInfo.h
@@ -20,6 +20,7 @@
 
 namespace clang {
 namespace find_all_symbols {
+struct SymbolFindings;
 
 /// \brief Contains all information for a Symbol.
 class SymbolInfo {
@@ -46,13 +47,32 @@
   /// \brief A pair of <ContextType, ContextName>.
   typedef std::pair<ContextType, std::string> Context;
 
+  // \brief SymbolFindings are signals gathered by observing how a symbol is
+  // used. These are used to rank results.
+  struct Signals {
+    Signals() {}
+    Signals(unsigned Seen, unsigned Used) : Seen(Seen), Used(Used) {}
+
+    // Number of times this symbol was visible to a TU.
+    unsigned Seen = 0;
+
+    // Number of times this symbol was referenced a TU's main file.
+    unsigned Used = 0;
+
+    Signals &operator+=(const Signals &RHS);
+    Signals operator+(const Signals &RHS) const;
+  };
+
+  using WithSignals = std::pair<SymbolInfo, Signals>;
+  using SignalMap = std::map<SymbolInfo, Signals>;
+
   // The default constructor is required by YAML traits in
   // LLVM_YAML_IS_DOCUMENT_LIST_VECTOR.
   SymbolInfo() : Type(SymbolKind::Unknown), LineNumber(-1) {}
 
   SymbolInfo(llvm::StringRef Name, SymbolKind Type, llvm::StringRef FilePath,
              int LineNumber, const std::vector<Context> &Contexts,
-             unsigned NumOccurrences = 0);
+             unsigned NumOccurrences = 0, unsigned NumUses = 0);
 
   void SetFilePath(llvm::StringRef Path) { FilePath = Path; }
 
@@ -70,21 +90,17 @@
 
   /// \brief Get symbol contexts.
   const std::vector<SymbolInfo::Context> &getContexts() const {
-    return Contexts;
-  }
+    return Contexts; }
 
-  /// \brief Get a 1-based line number of the symbol's declaration.
-  int getLineNumber() const { return LineNumber; }
+/// \brief Get a 1-based line number of the symbol's declaration.
+int getLineNumber() const { return LineNumber; }
 
-  /// \brief The number of times this symbol was found during an indexing run.
-  unsigned getNumOccurrences() const { return NumOccurrences; }
+bool operator<(const SymbolInfo &Symbol) const;
 
-  bool operator<(const SymbolInfo &Symbol) const;
-
-  bool operator==(const SymbolInfo &Symbol) const;
+bool operator==(const SymbolInfo &Symbol) const;
 
 private:
-  friend struct llvm::yaml::MappingTraits<SymbolInfo>;
+  friend struct llvm::yaml::MappingTraits<WithSignals>;
 
   /// \brief Identifier name.
   std::string Name;
@@ -110,18 +126,15 @@
 
   /// \brief The 1-based line number of of the symbol's declaration.
   int LineNumber;
-
-  /// \brief The number of times this symbol was found during an indexing
-  /// run. Populated by the reducer and used to rank results.
-  unsigned NumOccurrences;
 };
 
 /// \brief Write SymbolInfos to a stream (YAML format).
 bool WriteSymbolInfosToStream(llvm::raw_ostream &OS,
-                              const std::set<SymbolInfo> &Symbols);
+                              const SymbolInfo::SignalMap &Symbols);
 
 /// \brief Read SymbolInfos from a YAML document.
-std::vector<SymbolInfo> ReadSymbolInfosFromYAML(llvm::StringRef Yaml);
+std::vector<SymbolInfo::WithSignals>
+ReadSymbolInfosFromYAML(llvm::StringRef Yaml);
 
 } // namespace find_all_symbols
 } // namespace clang
Index: include-fixer/find-all-symbols/SymbolInfo.cpp
===================================================================
--- include-fixer/find-all-symbols/SymbolInfo.cpp
+++ include-fixer/find-all-symbols/SymbolInfo.cpp
@@ -20,20 +20,21 @@
 using clang::find_all_symbols::SymbolInfo;
 using SymbolKind = clang::find_all_symbols::SymbolInfo::SymbolKind;
 
-LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(SymbolInfo)
+LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(SymbolInfo::WithSignals)
 LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(std::string)
 LLVM_YAML_IS_SEQUENCE_VECTOR(SymbolInfo::Context)
 
 namespace llvm {
 namespace yaml {
-template <> struct MappingTraits<SymbolInfo> {
-  static void mapping(IO &io, SymbolInfo &Symbol) {
-    io.mapRequired("Name", Symbol.Name);
-    io.mapRequired("Contexts", Symbol.Contexts);
-    io.mapRequired("FilePath", Symbol.FilePath);
-    io.mapRequired("LineNumber", Symbol.LineNumber);
-    io.mapRequired("Type", Symbol.Type);
-    io.mapRequired("NumOccurrences", Symbol.NumOccurrences);
+template <> struct MappingTraits<SymbolInfo::WithSignals> {
+  static void mapping(IO &io, SymbolInfo::WithSignals &Symbol) {
+    io.mapRequired("Name", Symbol.first.Name);
+    io.mapRequired("Contexts", Symbol.first.Contexts);
+    io.mapRequired("FilePath", Symbol.first.FilePath);
+    io.mapRequired("LineNumber", Symbol.first.LineNumber);
+    io.mapRequired("Type", Symbol.first.Type);
+    io.mapRequired("Seen", Symbol.second.Seen);
+    io.mapRequired("Used", Symbol.second.Used);
   }
 };
 
@@ -74,9 +75,9 @@
 SymbolInfo::SymbolInfo(llvm::StringRef Name, SymbolKind Type,
                        llvm::StringRef FilePath, int LineNumber,
                        const std::vector<Context> &Contexts,
-                       unsigned NumOccurrences)
+                       unsigned NumOccurrences, unsigned NumUses)
     : Name(Name), Type(Type), FilePath(FilePath), Contexts(Contexts),
-      LineNumber(LineNumber), NumOccurrences(NumOccurrences) {}
+      LineNumber(LineNumber) {}
 
 bool SymbolInfo::operator==(const SymbolInfo &Symbol) const {
   return std::tie(Name, Type, FilePath, LineNumber, Contexts) ==
@@ -100,16 +101,29 @@
   return QualifiedName;
 }
 
+SymbolInfo::Signals& SymbolInfo::Signals::operator+=(const Signals& RHS) {
+  Seen += RHS.Seen;
+  Used += RHS.Used;
+  return *this;
+}
+
+SymbolInfo::Signals SymbolInfo::Signals::operator+(const Signals& RHS) const {
+  Signals Result = *this;
+  Result += RHS;
+  return Result;
+}
+
 bool WriteSymbolInfosToStream(llvm::raw_ostream &OS,
-                              const std::set<SymbolInfo> &Symbols) {
+                              const SymbolInfo::SignalMap &Symbols) {
   llvm::yaml::Output yout(OS);
-  for (auto Symbol : Symbols)
+  for (SymbolInfo::WithSignals Symbol : Symbols)
     yout << Symbol;
   return true;
 }
 
-std::vector<SymbolInfo> ReadSymbolInfosFromYAML(llvm::StringRef Yaml) {
-  std::vector<SymbolInfo> Symbols;
+std::vector<SymbolInfo::WithSignals>
+ReadSymbolInfosFromYAML(llvm::StringRef Yaml) {
+  std::vector<SymbolInfo::WithSignals> Symbols;
   llvm::yaml::Input yin(Yaml);
   yin >> Symbols;
   return Symbols;
Index: include-fixer/find-all-symbols/FindAllSymbols.h
===================================================================
--- include-fixer/find-all-symbols/FindAllSymbols.h
+++ include-fixer/find-all-symbols/FindAllSymbols.h
@@ -43,7 +43,14 @@
   void
   run(const clang::ast_matchers::MatchFinder::MatchResult &result) override;
 
+protected:
+  void onEndOfTranslationUnit() override;
+
 private:
+  // Current source file being processed, filled by first symbol found.
+  std::string Filename;
+  // Findings for the current source file, flushed on EndSourceFileAction.
+  SymbolInfo::SignalMap FileSymbols;
   // Reporter for SymbolInfo.
   SymbolReporter *const Reporter;
   // A remapping header file collector allowing clients include a different
Index: include-fixer/find-all-symbols/FindAllSymbols.cpp
===================================================================
--- include-fixer/find-all-symbols/FindAllSymbols.cpp
+++ include-fixer/find-all-symbols/FindAllSymbols.cpp
@@ -154,64 +154,84 @@
   // The float parameter of the function pointer has an empty name, and its
   // declaration context is an anonymous namespace; therefore, it won't be
   // filtered out by our matchers above.
-  MatchFinder->addMatcher(varDecl(CommonFilter,
-                                  anyOf(ExternCMatcher, CCMatcher),
-                                  unless(parmVarDecl()))
-                              .bind("decl"),
-                          this);
+  auto Vars = varDecl(CommonFilter, anyOf(ExternCMatcher, CCMatcher),
+                      unless(parmVarDecl()));
 
   // Matchers for C-style record declarations in extern "C" {...}.
-  MatchFinder->addMatcher(
-      recordDecl(CommonFilter, ExternCMatcher, isDefinition()).bind("decl"),
-      this);
-
+  auto CRecords = recordDecl(CommonFilter, ExternCMatcher, isDefinition());
   // Matchers for C++ record declarations.
-  auto CxxRecordDecl =
-      cxxRecordDecl(CommonFilter, CCMatcher, isDefinition());
-  MatchFinder->addMatcher(CxxRecordDecl.bind("decl"), this);
+  auto CXXRecords = cxxRecordDecl(CommonFilter, CCMatcher, isDefinition());
 
   // Matchers for function declarations.
   // We want to exclude friend declaration, but the `DeclContext` of a friend
   // function declaration is not the class in which it is declared, so we need
   // to explicitly check if the parent is a `friendDecl`.
-  MatchFinder->addMatcher(functionDecl(CommonFilter,
-                                       unless(hasParent(friendDecl())),
-                                       anyOf(ExternCMatcher, CCMatcher))
-                              .bind("decl"),
-                          this);
+  auto Functions = functionDecl(CommonFilter, unless(hasParent(friendDecl())),
+                                anyOf(ExternCMatcher, CCMatcher));
 
   // Matcher for typedef and type alias declarations.
   //
-  // typedef and type alias can come from C-style headers and C++ heaeders.
-  // For C-style header, `DeclContxet` can be either `TranslationUnitDecl`
+  // typedef and type alias can come from C-style headers and C++ headers.
+  // For C-style headers, `DeclContxet` can be either `TranslationUnitDecl`
   // or `LinkageSpecDecl`.
-  // For C++ header, `DeclContext ` can be one of `TranslationUnitDecl`,
-  // `NamespaceDecl`.
+  // For C++ headers, `DeclContext ` can be either `TranslationUnitDecl`
+  // or `NamespaceDecl`.
   // With the following context matcher, we can match `typedefNameDecl` from
-  // both C-style header and C++ header (except for those in classes).
+  // both C-style headers and C++ headers (except for those in classes).
   // "cc_matchers" are not included since template-related matchers are not
   // applicable on `TypedefNameDecl`.
-  MatchFinder->addMatcher(
+  auto Typedefs =
       typedefNameDecl(CommonFilter, anyOf(HasNSOrTUCtxMatcher,
-                                          hasDeclContext(linkageSpecDecl())))
-          .bind("decl"),
-      this);
+                                          hasDeclContext(linkageSpecDecl())));
 
   // Matchers for enum declarations.
-  MatchFinder->addMatcher(enumDecl(CommonFilter, isDefinition(),
-                                   anyOf(HasNSOrTUCtxMatcher, ExternCMatcher))
-                              .bind("decl"),
-                          this);
+  auto Enums = enumDecl(CommonFilter, isDefinition(),
+                        anyOf(HasNSOrTUCtxMatcher, ExternCMatcher));
 
   // Matchers for enum constant declarations.
   // We only match the enum constants in non-scoped enum declarations which are
   // inside toplevel translation unit or a namespace.
+  auto EnumConstants = enumConstantDecl(
+      CommonFilter, unless(isInScopedEnum()),
+      anyOf(hasDeclContext(enumDecl(HasNSOrTUCtxMatcher)), ExternCMatcher));
+
+  // Most of the time we care about all matchable decls, or all types.
+  auto Types = namedDecl(anyOf(CRecords, CXXRecords, Enums, Typedefs));
+  auto Decls = namedDecl(anyOf(CRecords, CXXRecords, Enums, Typedefs, Vars,
+                               EnumConstants, Functions));
+
+  // We want eligible decls bound to "decl"...
+  MatchFinder->addMatcher(Decls.bind("decl"), this);
+
+  // ... and all uses of them bound to "use". These have many cases:
+  // Uses of values/functions: these generate a declRefExpr.
+  MatchFinder->addMatcher(
+      declRefExpr(isExpansionInMainFile(), to(Decls.bind("use"))), this);
+  // Uses of function templates:
+  MatchFinder->addMatcher(
+      declRefExpr(isExpansionInMainFile(),
+                  to(functionDecl(hasParent(
+                      functionTemplateDecl(has(Functions.bind("use"))))))),
+      this);
+
+  // Uses of most types: just look at what the typeLoc refers to.
+  MatchFinder->addMatcher(
+      typeLoc(isExpansionInMainFile(),
+              loc(qualType(hasDeclaration(Types.bind("use"))))),
+      this);
+  // Uses of typedefs: these are transparent to hasDeclaration, so we need to
+  // handle them explicitly.
   MatchFinder->addMatcher(
-      enumConstantDecl(
-          CommonFilter,
-          unless(isInScopedEnum()),
-          anyOf(hasDeclContext(enumDecl(HasNSOrTUCtxMatcher)), ExternCMatcher))
-          .bind("decl"),
+      typeLoc(isExpansionInMainFile(),
+              loc(typedefType(hasDeclaration(Typedefs.bind("use"))))),
+      this);
+  // Uses of class templates:
+  // The typeLoc names the templateSpecializationType. Its declaration is the
+  // ClassTemplateDecl, which contains the CXXRecordDecl we want.
+  MatchFinder->addMatcher(
+      typeLoc(isExpansionInMainFile(),
+              loc(templateSpecializationType(hasDeclaration(
+                  classTemplateDecl(has(CXXRecords.bind("use"))))))),
       this);
 }
 
@@ -221,15 +241,29 @@
     return;
   }
 
-  const auto *ND = Result.Nodes.getNodeAs<NamedDecl>("decl");
-  assert(ND && "Matched declaration must be a NamedDecl!");
+  SymbolInfo::Signals Signals;
+  const NamedDecl *ND;
+  if ((ND = Result.Nodes.getNodeAs<NamedDecl>("use"))) {
+    Signals.Used = 1;
+  } else if((ND = Result.Nodes.getNodeAs<NamedDecl>("decl"))) {
+    Signals.Seen = 1;
+  } else {
+    assert(false && "Must match a NamedDecl!");
+  }
+
   const SourceManager *SM = Result.SourceManager;
+  if (auto Symbol = CreateSymbolInfo(ND, *SM, Collector)) {
+    Filename = SM->getFileEntryForID(SM->getMainFileID())->getName();
+    FileSymbols[*Symbol] += Signals;
+  }
+}
 
-  llvm::Optional<SymbolInfo> Symbol =
-      CreateSymbolInfo(ND, *SM, Collector);
-  if (Symbol)
-    Reporter->reportSymbol(
-        SM->getFileEntryForID(SM->getMainFileID())->getName(), *Symbol);
+void FindAllSymbols::onEndOfTranslationUnit() {
+  if (Filename != "") {
+    Reporter->reportSymbols(Filename, std::move(FileSymbols));
+    FileSymbols = {};
+    Filename = "";
+  }
 }
 
 } // namespace find_all_symbols
Index: include-fixer/find-all-symbols/FindAllMacros.h
===================================================================
--- include-fixer/find-all-symbols/FindAllMacros.h
+++ include-fixer/find-all-symbols/FindAllMacros.h
@@ -16,6 +16,7 @@
 #include "clang/Lex/PPCallbacks.h"
 
 namespace clang {
+class MacroInfo;
 namespace find_all_symbols {
 
 class HeaderMapCollector;
@@ -32,7 +33,24 @@
   void MacroDefined(const Token &MacroNameTok,
                     const MacroDirective *MD) override;
 
+  void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD,
+                    SourceRange Range, const MacroArgs *Args) override;
+
+  void Ifdef(SourceLocation Loc, const Token &MacroNameTok,
+             const MacroDefinition &MD) override;
+
+  void Ifndef(SourceLocation Loc, const Token &MacroNameTok,
+              const MacroDefinition &MD) override;
+
+  void EndOfMainFile() override;
+
 private:
+  llvm::Optional<SymbolInfo> CreateMacroSymbol(const Token &MacroNameTok,
+                                               const MacroInfo *MD);
+  // Not a callback, just a common path for all usage types.
+  void MacroUsed(const Token &Name, const MacroDefinition &MD);
+
+  SymbolInfo::SignalMap FileSymbols;
   // Reporter for SymbolInfo.
   SymbolReporter *const Reporter;
   SourceManager *const SM;
Index: include-fixer/find-all-symbols/FindAllMacros.cpp
===================================================================
--- include-fixer/find-all-symbols/FindAllMacros.cpp
+++ include-fixer/find-all-symbols/FindAllMacros.cpp
@@ -13,24 +13,60 @@
 #include "SymbolInfo.h"
 #include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Lex/MacroInfo.h"
 #include "clang/Lex/Token.h"
 #include "llvm/Support/Path.h"
 
 namespace clang {
 namespace find_all_symbols {
 
+llvm::Optional<SymbolInfo>
+FindAllMacros::CreateMacroSymbol(const Token &MacroNameTok,
+                                 const MacroInfo *info) {
+  std::string FilePath =
+      getIncludePath(*SM, info->getDefinitionLoc(), Collector);
+  if (FilePath.empty()) return llvm::None;
+  return SymbolInfo(MacroNameTok.getIdentifierInfo()->getName(),
+                    SymbolInfo::SymbolKind::Macro, FilePath,
+                    SM->getSpellingLineNumber(info->getDefinitionLoc()), {});
+}
+
 void FindAllMacros::MacroDefined(const Token &MacroNameTok,
                                  const MacroDirective *MD) {
-  SourceLocation Loc = SM->getExpansionLoc(MacroNameTok.getLocation());
-  std::string FilePath = getIncludePath(*SM, Loc, Collector);
-  if (FilePath.empty()) return;
+  if (auto Symbol = CreateMacroSymbol(MacroNameTok, MD->getMacroInfo())) {
+    FileSymbols[*Symbol].Seen++;
+  }
+}
 
-  SymbolInfo Symbol(MacroNameTok.getIdentifierInfo()->getName(),
-                    SymbolInfo::SymbolKind::Macro, FilePath,
-                    SM->getSpellingLineNumber(Loc), {});
+void FindAllMacros::MacroUsed(const Token &Name, const MacroDefinition &MD) {
+  if (!MD || !SM->isInMainFile(SM->getExpansionLoc(Name.getLocation()))) {
+    return;
+  }
+  if (auto Symbol = CreateMacroSymbol(Name, MD.getMacroInfo())) {
+    FileSymbols[*Symbol].Used++;
+  }
+}
+
+void FindAllMacros::MacroExpands(const Token& MacroNameTok,
+                                 const MacroDefinition &MD, SourceRange Range,
+                                 const MacroArgs *Args) {
+  MacroUsed(MacroNameTok, MD);
+}
+
+void FindAllMacros::Ifdef(SourceLocation Loc, const Token &MacroNameTok,
+                          const MacroDefinition &MD) {
+  MacroUsed(MacroNameTok, MD);
+}
+
+void FindAllMacros::Ifndef(SourceLocation Loc, const Token &MacroNameTok,
+                           const MacroDefinition &MD) {
+  MacroUsed(MacroNameTok, MD);
+}
 
-  Reporter->reportSymbol(SM->getFileEntryForID(SM->getMainFileID())->getName(),
-                         Symbol);
+void FindAllMacros::EndOfMainFile() {
+  Reporter->reportSymbols(SM->getFileEntryForID(SM->getMainFileID())->getName(),
+                          FileSymbols);
+  FileSymbols = {};
 }
 
 } // namespace find_all_symbols
Index: include-fixer/YamlSymbolIndex.h
===================================================================
--- include-fixer/YamlSymbolIndex.h
+++ include-fixer/YamlSymbolIndex.h
@@ -29,15 +29,15 @@
   static llvm::ErrorOr<std::unique_ptr<YamlSymbolIndex>>
   createFromDirectory(llvm::StringRef Directory, llvm::StringRef Name);
 
-  std::vector<clang::find_all_symbols::SymbolInfo>
+  std::vector<find_all_symbols::SymbolInfo::WithSignals>
   search(llvm::StringRef Identifier) override;
 
 private:
   explicit YamlSymbolIndex(
-      std::vector<clang::find_all_symbols::SymbolInfo> Symbols)
+      std::vector<find_all_symbols::SymbolInfo::WithSignals> Symbols)
       : Symbols(std::move(Symbols)) {}
 
-  std::vector<clang::find_all_symbols::SymbolInfo> Symbols;
+  std::vector<find_all_symbols::SymbolInfo::WithSignals> Symbols;
 };
 
 } // namespace include_fixer
Index: include-fixer/YamlSymbolIndex.cpp
===================================================================
--- include-fixer/YamlSymbolIndex.cpp
+++ include-fixer/YamlSymbolIndex.cpp
@@ -47,10 +47,11 @@
   return llvm::make_error_code(llvm::errc::no_such_file_or_directory);
 }
 
-std::vector<SymbolInfo> YamlSymbolIndex::search(llvm::StringRef Identifier) {
-  std::vector<SymbolInfo> Results;
+std::vector<SymbolInfo::WithSignals>
+YamlSymbolIndex::search(llvm::StringRef Identifier) {
+  std::vector<SymbolInfo::WithSignals> Results;
   for (const auto &Symbol : Symbols) {
-    if (Symbol.getName() == Identifier)
+    if (Symbol.first.getName() == Identifier)
       Results.push_back(Symbol);
   }
   return Results;
Index: include-fixer/SymbolIndexManager.cpp
===================================================================
--- include-fixer/SymbolIndexManager.cpp
+++ include-fixer/SymbolIndexManager.cpp
@@ -45,27 +45,28 @@
   return MaxSegments;
 }
 
-static void rank(std::vector<SymbolInfo> &Symbols,
+static void rank(std::vector<SymbolInfo::WithSignals> &Symbols,
                  llvm::StringRef FileName) {
   llvm::DenseMap<llvm::StringRef, double> Score;
-  for (const SymbolInfo &Symbol : Symbols) {
+  for (const auto &Symbol : Symbols) {
     // Calculate a score from the similarity of the header the symbol is in
     // with the current file and the popularity of the symbol.
-    double NewScore = similarityScore(FileName, Symbol.getFilePath()) *
-                      (1.0 + std::log2(1 + Symbol.getNumOccurrences()));
-    double &S = Score[Symbol.getFilePath()];
+    double NewScore = similarityScore(FileName, Symbol.first.getFilePath()) *
+                      (1.0 + std::log2(1 + Symbol.second.Seen));
+    double &S = Score[Symbol.first.getFilePath()];
     S = std::max(S, NewScore);
   }
   // Sort by the gathered scores. Use file name as a tie breaker so we can
   // deduplicate.
-  std::sort(Symbols.begin(), Symbols.end(),
-            [&](const SymbolInfo &A, const SymbolInfo &B) {
-              auto AS = Score[A.getFilePath()];
-              auto BS = Score[B.getFilePath()];
-              if (AS != BS)
-                return AS > BS;
-              return A.getFilePath() < B.getFilePath();
-            });
+  std::sort(
+      Symbols.begin(), Symbols.end(),
+      [&](const SymbolInfo::WithSignals &A, const SymbolInfo::WithSignals &B) {
+        auto AS = Score[A.first.getFilePath()];
+        auto BS = Score[B.first.getFilePath()];
+        if (AS != BS)
+          return AS > BS;
+        return A.first.getFilePath() < B.first.getFilePath();
+      });
 }
 
 std::vector<find_all_symbols::SymbolInfo>
@@ -88,18 +89,19 @@
   // Eventually we will either hit a class (namespaces aren't in the database
   // either) and can report that result.
   bool TookPrefix = false;
-  std::vector<clang::find_all_symbols::SymbolInfo> MatchedSymbols;
+  std::vector<SymbolInfo::WithSignals> MatchedSymbols;
   do {
-    std::vector<clang::find_all_symbols::SymbolInfo> Symbols;
+    std::vector<SymbolInfo::WithSignals> Symbols;
     for (const auto &DB : SymbolIndices) {
       auto Res = DB.get()->search(Names.back());
       Symbols.insert(Symbols.end(), Res.begin(), Res.end());
     }
 
     DEBUG(llvm::dbgs() << "Searching " << Names.back() << "... got "
                        << Symbols.size() << " results...\n");
 
-    for (const auto &Symbol : Symbols) {
+    for (const auto &SymAndSig : Symbols) {
+      const SymbolInfo Symbol = SymAndSig.first;
       // Match the identifier name without qualifier.
       if (Symbol.getName() == Names.back()) {
         bool IsMatched = true;
@@ -139,16 +141,19 @@
                Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Macro))
             continue;
 
-          MatchedSymbols.push_back(Symbol);
+          MatchedSymbols.push_back(SymAndSig);
         }
       }
     }
     Names.pop_back();
     TookPrefix = true;
   } while (MatchedSymbols.empty() && !Names.empty() && IsNestedSearch);
 
   rank(MatchedSymbols, FileName);
-  return MatchedSymbols;
+  // Strip signals, they are no longer needed.
+  std::vector<SymbolInfo> Res;
+  for (const auto& SymAndSig : MatchedSymbols) Res.push_back(SymAndSig.first);
+  return Res;
 }
 
 } // namespace include_fixer
Index: include-fixer/SymbolIndex.h
===================================================================
--- include-fixer/SymbolIndex.h
+++ include-fixer/SymbolIndex.h
@@ -28,7 +28,7 @@
   /// \returns A list of `SymbolInfo` candidates.
   // FIXME: Expose the type name so we can also insert using declarations (or
   // fix the usage)
-  virtual std::vector<clang::find_all_symbols::SymbolInfo>
+  virtual std::vector<clang::find_all_symbols::SymbolInfo::WithSignals>
   search(llvm::StringRef Identifier) = 0;
 };
 
Index: include-fixer/IncludeFixer.cpp
===================================================================
--- include-fixer/IncludeFixer.cpp
+++ include-fixer/IncludeFixer.cpp
@@ -334,8 +334,7 @@
         SourceManager, HeaderSearch);
     SymbolCandidates.emplace_back(Symbol.getName(), Symbol.getSymbolKind(),
                                   MinimizedFilePath, Symbol.getLineNumber(),
-                                  Symbol.getContexts(),
-                                  Symbol.getNumOccurrences());
+                                  Symbol.getContexts());
   }
   return IncludeFixerContext(FilePath, QuerySymbolInfos, SymbolCandidates);
 }
Index: include-fixer/InMemorySymbolIndex.h
===================================================================
--- include-fixer/InMemorySymbolIndex.h
+++ include-fixer/InMemorySymbolIndex.h
@@ -21,13 +21,15 @@
 /// Xref database with fixed content.
 class InMemorySymbolIndex : public SymbolIndex {
 public:
-  InMemorySymbolIndex(const std::vector<find_all_symbols::SymbolInfo> &Symbols);
+  InMemorySymbolIndex(
+      const std::vector<find_all_symbols::SymbolInfo::WithSignals> &Symbols);
 
-  std::vector<clang::find_all_symbols::SymbolInfo>
+  std::vector<clang::find_all_symbols::SymbolInfo::WithSignals>
   search(llvm::StringRef Identifier) override;
 
 private:
-  std::map<std::string, std::vector<clang::find_all_symbols::SymbolInfo>>
+  std::map<std::string,
+           std::vector<clang::find_all_symbols::SymbolInfo::WithSignals>>
       LookupTable;
 };
 
Index: include-fixer/InMemorySymbolIndex.cpp
===================================================================
--- include-fixer/InMemorySymbolIndex.cpp
+++ include-fixer/InMemorySymbolIndex.cpp
@@ -15,12 +15,12 @@
 namespace include_fixer {
 
 InMemorySymbolIndex::InMemorySymbolIndex(
-    const std::vector<SymbolInfo> &Symbols) {
+    const std::vector<SymbolInfo::WithSignals> &Symbols) {
   for (const auto &Symbol : Symbols)
-    LookupTable[Symbol.getName()].push_back(Symbol);
+    LookupTable[Symbol.first.getName()].push_back(Symbol);
 }
 
-std::vector<SymbolInfo>
+std::vector<SymbolInfo::WithSignals>
 InMemorySymbolIndex::search(llvm::StringRef Identifier) {
   auto I = LookupTable.find(Identifier);
   if (I != LookupTable.end())
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to