Index: include-fixer/SymbolIndexManager.cpp =================================================================== --- include-fixer/SymbolIndexManager.cpp +++ include-fixer/SymbolIndexManager.cpp @@ -17,6 +17,37 @@ namespace clang { namespace include_fixer { +using clang::find_all_symbols::SymbolInfo; + +/// Sorts and uniques SymbolInfos based on the popularity info in SymbolInfo. +static void rankByPopularity(std::vector &Symbols) { + // First collect occurrences per header file. + std::map HeaderPopularity; + for (const SymbolInfo &Symbol : Symbols) { + unsigned &Popularity = HeaderPopularity[Symbol.getFilePath()]; + Popularity = std::max(Popularity, Symbol.getNumOccurrences()); + } + + // Sort by the gathered popularities. Use file name as a tie breaker so we can + // deduplicate. + std::sort(Symbols.begin(), Symbols.end(), + [&](const SymbolInfo &A, const SymbolInfo &B) { + auto APop = HeaderPopularity[A.getFilePath()]; + auto BPop = HeaderPopularity[B.getFilePath()]; + if (APop != BPop) + return APop > BPop; + return A.getFilePath() < B.getFilePath(); + }); + + // Deduplicate based on the file name. They will have the same popularity and + // we don't want to suggest the same header twice. + Symbols.erase(std::unique(Symbols.begin(), Symbols.end(), + [](const SymbolInfo &A, const SymbolInfo &B) { + return A.getFilePath() == B.getFilePath(); + }), + Symbols.end()); +} + std::vector SymbolIndexManager::search(llvm::StringRef Identifier) const { // The identifier may be fully qualified, so split it and get all the context @@ -45,6 +76,8 @@ DEBUG(llvm::dbgs() << "Searching " << Names.back() << "... got " << Symbols.size() << " results...\n"); + rankByPopularity(Symbols); + for (const auto &Symbol : Symbols) { // Match the identifier name without qualifier. if (Symbol.getName() == Names.back()) { Index: include-fixer/tool/ClangIncludeFixer.cpp =================================================================== --- include-fixer/tool/ClangIncludeFixer.cpp +++ include-fixer/tool/ClangIncludeFixer.cpp @@ -98,11 +98,11 @@ std::vector Headers; SmallVector CommaSplits; Split.second.split(CommaSplits, ","); - for (StringRef Header : CommaSplits) + for (const StringRef &Header : CommaSplits) Symbols.push_back(find_all_symbols::SymbolInfo( Split.first.trim(), find_all_symbols::SymbolInfo::SymbolKind::Unknown, Header.trim(), 1, - {})); + {}, /*NumOccurrences=*/CommaSplits.end() - &Header)); } SymbolIndexMgr->addSymbolIndex( llvm::make_unique(Symbols)); 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 @@ -22,3 +22,24 @@ Type: Class NumOccurrences: 1 ... +Name: bar +Contexts: + - ContextType: Namespace + ContextName: a + - ContextType: Namespace + ContextName: b +FilePath: ../include/bar.h +LineNumber: 2 +Type: Class +NumOccurrences: 3 +... +Name: bar +Contexts: + - ContextType: Namespace + ContextName: a + - ContextType: Namespace + ContextName: b +FilePath: ../include/zbar.h +LineNumber: 1 +Type: Class +NumOccurrences: 3 Index: test/include-fixer/ranking.cpp =================================================================== --- /dev/null +++ test/include-fixer/ranking.cpp @@ -0,0 +1,6 @@ +// RUN: clang-include-fixer -db=yaml -input=%S/Inputs/fake_yaml_db.yaml -output-headers %s -- | FileCheck %s -implicit-check-not=.h + +// CHECK: "../include/bar.h" +// CHECK-NEXT: "../include/zbar.h" + +bar b;