#include "SymbolIndexManager.h"
#include <cmath>
#include "find-all-symbols/SymbolInfo.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Path.h"
#define DEBUG_TYPE "clang-include-fixer"
namespace clang {
namespace include_fixer {
using find_all_symbols::SymbolInfo;
using find_all_symbols::SymbolAndSignals;
static double similarityScore(llvm::StringRef FileName,
llvm::StringRef Header) {
int MaxSegments = 1;
for (auto FileI = llvm::sys::path::begin(FileName),
FileE = llvm::sys::path::end(FileName);
FileI != FileE; ++FileI) {
int Segments = 0;
for (auto HeaderI = llvm::sys::path::begin(Header),
HeaderE = llvm::sys::path::end(Header), I = FileI;
HeaderI != HeaderE && *I == *HeaderI && I != FileE; ++I, ++HeaderI) {
++Segments;
}
MaxSegments = std::max(Segments, MaxSegments);
}
return MaxSegments;
}
static void rank(std::vector<SymbolAndSignals> &Symbols,
llvm::StringRef FileName) {
llvm::StringMap<double> Score;
for (const auto &Symbol : Symbols) {
double NewScore = similarityScore(FileName, Symbol.Symbol.getFilePath()) *
(1.0 + std::log2(1 + Symbol.Signals.Seen));
double &S = Score[Symbol.Symbol.getFilePath()];
S = std::max(S, NewScore);
}
llvm::sort(Symbols,
[&](const SymbolAndSignals &A, const SymbolAndSignals &B) {
auto AS = Score[A.Symbol.getFilePath()];
auto BS = Score[B.Symbol.getFilePath()];
if (AS != BS)
return AS > BS;
return A.Symbol.getFilePath() < B.Symbol.getFilePath();
});
}
std::vector<find_all_symbols::SymbolInfo>
SymbolIndexManager::search(llvm::StringRef Identifier,
bool IsNestedSearch,
llvm::StringRef FileName) const {
llvm::SmallVector<llvm::StringRef, 8> Names;
Identifier.split(Names, "::");
bool IsFullyQualified = false;
if (Identifier.starts_with("::")) {
Names.erase(Names.begin());
IsFullyQualified = true;
}
bool TookPrefix = false;
std::vector<SymbolAndSignals> MatchedSymbols;
do {
std::vector<SymbolAndSignals> Symbols;
for (const auto &DB : SymbolIndices) {
auto Res = DB.get()->search(Names.back());
Symbols.insert(Symbols.end(), Res.begin(), Res.end());
}
LLVM_DEBUG(llvm::dbgs() << "Searching " << Names.back() << "... got "
<< Symbols.size() << " results...\n");
for (auto &SymAndSig : Symbols) {
const SymbolInfo &Symbol = SymAndSig.Symbol;
bool IsMatched = true;
auto SymbolContext = Symbol.getContexts().begin();
auto IdentiferContext = Names.rbegin() + 1;
while (IdentiferContext != Names.rend() &&
SymbolContext != Symbol.getContexts().end()) {
if (SymbolContext->second == *IdentiferContext) {
++IdentiferContext;
++SymbolContext;
} else if (SymbolContext->first ==
find_all_symbols::SymbolInfo::ContextType::EnumDecl) {
++SymbolContext;
} else {
IsMatched = false;
break;
}
}
if (IsFullyQualified)
IsMatched &= (SymbolContext == Symbol.getContexts().end());
if (IsMatched && IdentiferContext == Names.rend()) {
if (TookPrefix &&
(Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Function ||
Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Variable ||
Symbol.getSymbolKind() ==
SymbolInfo::SymbolKind::EnumConstantDecl ||
Symbol.getSymbolKind() == SymbolInfo::SymbolKind::Macro))
continue;
MatchedSymbols.push_back(std::move(SymAndSig));
}
}
Names.pop_back();
TookPrefix = true;
} while (MatchedSymbols.empty() && !Names.empty() && IsNestedSearch);
rank(MatchedSymbols, FileName);
std::vector<SymbolInfo> Res;
Res.reserve(MatchedSymbols.size());
for (auto &SymAndSig : MatchedSymbols)
Res.push_back(std::move(SymAndSig.Symbol));
return Res;
}
}
}