#include "HeaderSourceSwitch.h"
#include "AST.h"
#include "SourceCode.h"
#include "index/SymbolCollector.h"
#include "support/Logger.h"
#include "support/Path.h"
#include "clang/AST/Decl.h"
#include <optional>
namespace clang {
namespace clangd {
std::optional<Path> getCorrespondingHeaderOrSource(
PathRef OriginalFile, llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
llvm::StringRef SourceExtensions[] = {".cpp", ".c", ".cc", ".cxx",
".c++", ".m", ".mm"};
llvm::StringRef HeaderExtensions[] = {".h", ".hh", ".hpp", ".hxx", ".inc"};
llvm::StringRef PathExt = llvm::sys::path::extension(OriginalFile);
bool IsSource = llvm::any_of(SourceExtensions, [&PathExt](PathRef SourceExt) {
return SourceExt.equals_insensitive(PathExt);
});
bool IsHeader = llvm::any_of(HeaderExtensions, [&PathExt](PathRef HeaderExt) {
return HeaderExt.equals_insensitive(PathExt);
});
if (!IsSource && !IsHeader)
return std::nullopt;
llvm::ArrayRef<llvm::StringRef> NewExts;
if (IsSource)
NewExts = HeaderExtensions;
else
NewExts = SourceExtensions;
llvm::SmallString<128> NewPath = OriginalFile;
for (llvm::StringRef NewExt : NewExts) {
llvm::sys::path::replace_extension(NewPath, NewExt);
if (VFS->exists(NewPath))
return Path(NewPath);
llvm::sys::path::replace_extension(NewPath, NewExt.upper());
if (VFS->exists(NewPath))
return Path(NewPath);
}
return std::nullopt;
}
std::optional<Path> getCorrespondingHeaderOrSource(PathRef OriginalFile,
ParsedAST &AST,
const SymbolIndex *Index) {
if (!Index) {
return std::nullopt;
}
LookupRequest Request;
for (const auto *D : getIndexableLocalDecls(AST)) {
if (auto ID = getSymbolID(D))
Request.IDs.insert(ID);
}
llvm::StringMap<int> Candidates;
auto AwardTarget = [&](const char *TargetURI) {
if (auto TargetPath = URI::resolve(TargetURI, OriginalFile)) {
if (!pathEqual(*TargetPath, OriginalFile))
++Candidates[*TargetPath];
} else {
elog("Failed to resolve URI {0}: {1}", TargetURI, TargetPath.takeError());
}
};
bool IsHeader = isHeaderFile(OriginalFile, AST.getLangOpts());
Index->lookup(Request, [&](const Symbol &Sym) {
if (IsHeader)
AwardTarget(Sym.Definition.FileURI);
else
AwardTarget(Sym.CanonicalDeclaration.FileURI);
});
if (Candidates.empty())
return std::nullopt;
auto Best = Candidates.begin();
for (auto It = Candidates.begin(); It != Candidates.end(); ++It) {
if (It->second > Best->second)
Best = It;
else if (It->second == Best->second && It->first() < Best->first())
Best = It;
}
return Path(Best->first());
}
std::vector<const Decl *> getIndexableLocalDecls(ParsedAST &AST) {
std::vector<const Decl *> Results;
std::function<void(Decl *)> TraverseDecl = [&](Decl *D) {
auto *ND = llvm::dyn_cast<NamedDecl>(D);
if (!ND || ND->isImplicit())
return;
if (!SymbolCollector::shouldCollectSymbol(*ND, D->getASTContext(), {},
false))
return;
if (!llvm::isa<FunctionDecl>(ND)) {
if (auto *Scope = llvm::dyn_cast<DeclContext>(ND)) {
for (auto *D : Scope->decls())
TraverseDecl(D);
}
}
if (llvm::isa<NamespaceDecl>(D))
return;
Results.push_back(D);
};
for (auto *TopLevel : AST.getLocalTopLevelDecls())
TraverseDecl(TopLevel);
return Results;
}
}
}