[clangd] Unify path canonicalizations in the codebase

Summary:
There were a few different places where we canonicalized paths, each
one had its own flavor. This patch tries to unify them all under one place.

Reviewers: ilya-biryukov

Subscribers: ioeric, MaskRay, jkorous, arphaman, cfe-commits

Differential Revision: https://reviews.llvm.org/D55818

llvm-svn: 349618
This commit is contained in:
Kadir Cetinkaya 2018-12-19 10:46:21 +00:00
parent 7120fb9b0b
commit dd67793c0c
5 changed files with 64 additions and 76 deletions

View File

@ -183,34 +183,49 @@ std::vector<TextEdit> replacementsToEdits(StringRef Code,
return Edits;
}
Optional<std::string> getRealPath(const FileEntry *F,
const SourceManager &SourceMgr) {
Optional<std::string> getCanonicalPath(const FileEntry *F,
const SourceManager &SourceMgr) {
if (!F)
return None;
// Ideally, we get the real path from the FileEntry object.
SmallString<128> FilePath = F->tryGetRealPathName();
if (!FilePath.empty()) {
if (!FilePath.empty() && sys::path::is_absolute(FilePath))
return FilePath.str().str();
}
// Otherwise, we try to compute ourselves.
vlog("FileEntry for {0} did not contain the real path.", F->getName());
FilePath = F->getName();
vlog("FileEntry for {0} did not contain the real path.", FilePath);
SmallString<128> Path = F->getName();
if (!sys::path::is_absolute(Path)) {
if (!SourceMgr.getFileManager().makeAbsolutePath(Path)) {
log("Could not turn relative path to absolute: {0}", Path);
if (!sys::path::is_absolute(FilePath)) {
if (auto EC =
SourceMgr.getFileManager().getVirtualFileSystem()->makeAbsolute(
FilePath)) {
elog("Could not turn relative path '{0}' to absolute: {1}", FilePath,
EC.message());
return None;
}
}
SmallString<128> RealPath;
if (SourceMgr.getFileManager().getVirtualFileSystem()->getRealPath(
Path, RealPath)) {
log("Could not compute real path: {0}", Path);
return Path.str().str();
// Handle the symbolic link path case where the current working directory
// (getCurrentWorkingDirectory) is a symlink./ We always want to the real
// file path (instead of the symlink path) for the C++ symbols.
//
// Consider the following example:
//
// src dir: /project/src/foo.h
// current working directory (symlink): /tmp/build -> /project/src/
//
// The file path of Symbol is "/project/src/foo.h" instead of
// "/tmp/build/foo.h"
if (const DirectoryEntry *Dir = SourceMgr.getFileManager().getDirectory(
sys::path::parent_path(FilePath))) {
SmallString<128> RealPath;
StringRef DirName = SourceMgr.getFileManager().getCanonicalName(Dir);
sys::path::append(RealPath, DirName, sys::path::filename(FilePath));
return RealPath.str().str();
}
return RealPath.str().str();
return FilePath.str().str();
}
TextEdit toTextEdit(const FixItHint &FixIt, const SourceManager &M,

View File

@ -78,17 +78,18 @@ std::vector<TextEdit> replacementsToEdits(StringRef Code,
TextEdit toTextEdit(const FixItHint &FixIt, const SourceManager &M,
const LangOptions &L);
/// Get the real/canonical path of \p F. This means:
/// Get the canonical path of \p F. This means:
///
/// - Absolute path
/// - Symlinks resolved
/// - No "." or ".." component
/// - No duplicate or trailing directory separator
///
/// This function should be used when sending paths to clients, so that paths
/// are normalized as much as possible.
llvm::Optional<std::string> getRealPath(const FileEntry *F,
const SourceManager &SourceMgr);
/// This function should be used when paths needs to be used outside the
/// component that generate it, so that paths are normalized as much as
/// possible.
llvm::Optional<std::string> getCanonicalPath(const FileEntry *F,
const SourceManager &SourceMgr);
bool IsRangeConsecutive(const Range &Left, const Range &Right);
} // namespace clangd

View File

@ -229,7 +229,7 @@ Optional<Location> makeLocation(ParsedAST &AST, SourceLocation TokLoc,
const FileEntry *F = SourceMgr.getFileEntryForID(SourceMgr.getFileID(TokLoc));
if (!F)
return None;
auto FilePath = getRealPath(F, SourceMgr);
auto FilePath = getCanonicalPath(F, SourceMgr);
if (!FilePath) {
log("failed to get path!");
return None;
@ -245,7 +245,8 @@ Optional<Location> makeLocation(ParsedAST &AST, SourceLocation TokLoc,
std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos,
const SymbolIndex *Index) {
const auto &SM = AST.getASTContext().getSourceManager();
auto MainFilePath = getRealPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
auto MainFilePath =
getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
if (!MainFilePath) {
elog("Failed to get a path for the main file, so no references");
return {};
@ -337,7 +338,7 @@ std::vector<Location> findDefinitions(ParsedAST &AST, Position Pos,
std::string TUPath;
const FileEntry *FE =
SM.getFileEntryForID(SM.getMainFileID());
if (auto Path = getRealPath(FE, SM))
if (auto Path = getCanonicalPath(FE, SM))
TUPath = *Path;
// Query the index and populate the empty slot.
Index->lookup(QueryRequest, [&TUPath, &ResultCandidates,
@ -708,7 +709,8 @@ std::vector<Location> findReferences(ParsedAST &AST, Position Pos,
const SymbolIndex *Index) {
std::vector<Location> Results;
const SourceManager &SM = AST.getASTContext().getSourceManager();
auto MainFilePath = getRealPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
auto MainFilePath =
getCanonicalPath(SM.getFileEntryForID(SM.getMainFileID()), SM);
if (!MainFilePath) {
elog("Failed to get a path for the main file, so no references");
return Results;

View File

@ -98,26 +98,20 @@ decltype(SymbolCollector::Options::FileFilter)
createFileFilter(const llvm::StringMap<FileDigest> &FileDigests,
llvm::StringMap<FileDigest> &FilesToUpdate) {
return [&FileDigests, &FilesToUpdate](const SourceManager &SM, FileID FID) {
StringRef Path;
if (const auto *F = SM.getFileEntryForID(FID))
Path = F->getName();
if (Path.empty())
const auto *F = SM.getFileEntryForID(FID);
if (!F)
return false; // Skip invalid files.
SmallString<128> AbsPath(Path);
if (std::error_code EC =
SM.getFileManager().getVirtualFileSystem()->makeAbsolute(AbsPath)) {
elog("Warning: could not make absolute file: {0}", EC.message());
auto AbsPath = getCanonicalPath(F, SM);
if (!AbsPath)
return false; // Skip files without absolute path.
}
sys::path::remove_dots(AbsPath, /*remove_dot_dot=*/true);
auto Digest = digestFile(SM, FID);
if (!Digest)
return false;
auto D = FileDigests.find(AbsPath);
auto D = FileDigests.find(*AbsPath);
if (D != FileDigests.end() && D->second == Digest)
return false; // Skip files that haven't changed.
FilesToUpdate[AbsPath] = *Digest;
FilesToUpdate[*AbsPath] = *Digest;
return true;
};
}

View File

@ -51,37 +51,17 @@ const NamedDecl &getTemplateOrThis(const NamedDecl &ND) {
//
// The Path can be a path relative to the build directory, or retrieved from
// the SourceManager.
Optional<std::string> toURI(const SourceManager &SM, StringRef Path,
const SymbolCollector::Options &Opts) {
SmallString<128> AbsolutePath(Path);
if (std::error_code EC =
SM.getFileManager().getVirtualFileSystem()->makeAbsolute(
AbsolutePath))
log("Warning: could not make absolute file: {0}", EC.message());
if (sys::path::is_absolute(AbsolutePath)) {
// Handle the symbolic link path case where the current working directory
// (getCurrentWorkingDirectory) is a symlink./ We always want to the real
// file path (instead of the symlink path) for the C++ symbols.
//
// Consider the following example:
//
// src dir: /project/src/foo.h
// current working directory (symlink): /tmp/build -> /project/src/
//
// The file path of Symbol is "/project/src/foo.h" instead of
// "/tmp/build/foo.h"
if (const DirectoryEntry *Dir = SM.getFileManager().getDirectory(
sys::path::parent_path(AbsolutePath.str()))) {
StringRef DirName = SM.getFileManager().getCanonicalName(Dir);
SmallString<128> AbsoluteFilename;
sys::path::append(AbsoluteFilename, DirName,
sys::path::filename(AbsolutePath.str()));
AbsolutePath = AbsoluteFilename;
}
} else if (!Opts.FallbackDir.empty()) {
sys::fs::make_absolute(Opts.FallbackDir, AbsolutePath);
std::string toURI(const SourceManager &SM, llvm::StringRef Path,
const SymbolCollector::Options &Opts) {
llvm::SmallString<128> AbsolutePath(Path);
if (auto CanonPath =
getCanonicalPath(SM.getFileManager().getFile(Path), SM)) {
AbsolutePath = *CanonPath;
}
// We don't perform is_absolute check in an else branch because makeAbsolute
// might return a relative path on some InMemoryFileSystems.
if (!sys::path::is_absolute(AbsolutePath) && !Opts.FallbackDir.empty())
sys::fs::make_absolute(Opts.FallbackDir, AbsolutePath);
sys::path::remove_dots(AbsolutePath, /*remove_dot_dot=*/true);
return URI::create(AbsolutePath).toString();
}
@ -211,17 +191,17 @@ Optional<SymbolLocation> getTokenLocation(SourceLocation TokLoc,
const SymbolCollector::Options &Opts,
const clang::LangOptions &LangOpts,
std::string &FileURIStorage) {
auto U = toURI(SM, SM.getFilename(TokLoc), Opts);
if (!U)
auto Path = SM.getFilename(TokLoc);
if (Path.empty())
return None;
FileURIStorage = std::move(*U);
FileURIStorage = toURI(SM, Path, Opts);
SymbolLocation Result;
Result.FileURI = FileURIStorage.c_str();
auto Range = getTokenRange(TokLoc, SM, LangOpts);
Result.Start = Range.first;
Result.End = Range.second;
return std::move(Result);
return Result;
}
// Checks whether \p ND is a definition of a TagDecl (class/struct/enum/union)
@ -487,11 +467,7 @@ void SymbolCollector::finish() {
if (Found == URICache.end()) {
if (auto *FileEntry = SM.getFileEntryForID(FID)) {
auto FileURI = toURI(SM, FileEntry->getName(), Opts);
if (!FileURI) {
log("Failed to create URI for file: {0}\n", FileEntry);
FileURI = ""; // reset to empty as we also want to cache this case.
}
Found = URICache.insert({FID, *FileURI}).first;
Found = URICache.insert({FID, FileURI}).first;
} else {
// Ignore cases where we can not find a corresponding file entry
// for the loc, thoses are not interesting, e.g. symbols formed