Skip to content

Commit

Permalink
Fix a buglet in remove_dots().
Browse files Browse the repository at this point in the history
The function promises to canonicalize the path, but neglected to do so
for the root component.

For example, calling remove_dots("/tmp/foo.c", Style::windows_backslash)
resulted in "/tmp\foo.c". Now it produces "\tmp\foo.c".

Also fix FIXME in the corresponding test.

Reviewed By: rnk

Differential Revision: https://reviews.llvm.org/D126412
  • Loading branch information
EmployedRussian authored and rnk committed Jun 2, 2022
1 parent e4870c8 commit 35ab2a1
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 19 deletions.
2 changes: 1 addition & 1 deletion clang/unittests/Basic/FileManagerTest.cpp
Expand Up @@ -411,7 +411,7 @@ TEST_F(FileManagerTest, getVirtualFileWithDifferentName) {
#endif // !_WIN32

static StringRef getSystemRoot() {
return is_style_windows(llvm::sys::path::Style::native) ? "C:/" : "/";
return is_style_windows(llvm::sys::path::Style::native) ? "C:\\" : "/";
}

TEST_F(FileManagerTest, makeAbsoluteUsesVFS) {
Expand Down
14 changes: 9 additions & 5 deletions clang/unittests/Frontend/PCHPreambleTest.cpp
Expand Up @@ -24,23 +24,27 @@ using namespace clang;

namespace {

std::string Canonicalize(const Twine &Path) {
SmallVector<char, 128> PathVec;
Path.toVector(PathVec);
llvm::sys::path::remove_dots(PathVec, true);
return std::string(PathVec.begin(), PathVec.end());
}

class ReadCountingInMemoryFileSystem : public vfs::InMemoryFileSystem
{
std::map<std::string, unsigned> ReadCounts;

public:
ErrorOr<std::unique_ptr<vfs::File>> openFileForRead(const Twine &Path) override
{
SmallVector<char, 128> PathVec;
Path.toVector(PathVec);
llvm::sys::path::remove_dots(PathVec, true);
++ReadCounts[std::string(PathVec.begin(), PathVec.end())];
++ReadCounts[Canonicalize(Path)];
return InMemoryFileSystem::openFileForRead(Path);
}

unsigned GetReadCount(const Twine &Path) const
{
auto it = ReadCounts.find(Path.str());
auto it = ReadCounts.find(Canonicalize(Path));
return it == ReadCounts.end() ? 0 : it->second;
}
};
Expand Down
6 changes: 5 additions & 1 deletion llvm/lib/Support/Path.cpp
Expand Up @@ -760,11 +760,15 @@ bool remove_dots(SmallVectorImpl<char> &the_path, bool remove_dot_dot,
}
}

SmallString<256> buffer = root;
// "root" could be "/", which may need to be translated into "\".
make_preferred(buffer, style);
needs_change |= root != buffer;

// Avoid rewriting the path unless we have to.
if (!needs_change)
return false;

SmallString<256> buffer = root;
if (!components.empty()) {
buffer += components[0];
for (StringRef C : makeArrayRef(components).drop_front()) {
Expand Down
15 changes: 7 additions & 8 deletions llvm/unittests/Support/Path.cpp
Expand Up @@ -1544,16 +1544,14 @@ TEST(Support, RemoveDots) {
EXPECT_EQ("C:\\a\\c", remove_dots("C:\\foo\\bar//..\\..\\a\\c", true,
path::Style::windows));

// FIXME: These leading forward slashes are emergent behavior. VFS depends on
// this behavior now.
EXPECT_EQ("C:/bar",
EXPECT_EQ("C:\\bar",
remove_dots("C:/foo/../bar", true, path::Style::windows));
EXPECT_EQ("C:/foo\\bar",
EXPECT_EQ("C:\\foo\\bar",
remove_dots("C:/foo/bar", true, path::Style::windows));
EXPECT_EQ("C:/foo\\bar",
EXPECT_EQ("C:\\foo\\bar",
remove_dots("C:/foo\\bar", true, path::Style::windows));
EXPECT_EQ("/", remove_dots("/", true, path::Style::windows));
EXPECT_EQ("C:/", remove_dots("C:/", true, path::Style::windows));
EXPECT_EQ("\\", remove_dots("/", true, path::Style::windows));
EXPECT_EQ("C:\\", remove_dots("C:/", true, path::Style::windows));

// Some clients of remove_dots expect it to remove trailing slashes. Again,
// this is emergent behavior that VFS relies on, and not inherently part of
Expand All @@ -1564,7 +1562,8 @@ TEST(Support, RemoveDots) {
remove_dots("/foo/bar/", true, path::Style::posix));

// A double separator is rewritten.
EXPECT_EQ("C:/foo\\bar", remove_dots("C:/foo//bar", true, path::Style::windows));
EXPECT_EQ("C:\\foo\\bar",
remove_dots("C:/foo//bar", true, path::Style::windows));

SmallString<64> Path1(".\\.\\c");
EXPECT_TRUE(path::remove_dots(Path1, true, path::Style::windows));
Expand Down
9 changes: 5 additions & 4 deletions llvm/unittests/Support/VirtualFileSystemTest.cpp
Expand Up @@ -199,7 +199,7 @@ class ErrorDummyFileSystem : public DummyFileSystem {
};

/// Replace back-slashes by front-slashes.
std::string getPosixPath(std::string S) {
std::string getPosixPath(const Twine &S) {
SmallString<128> Result;
llvm::sys::path::native(S, Result, llvm::sys::path::Style::posix);
return std::string(Result.str());
Expand Down Expand Up @@ -923,11 +923,11 @@ TEST(ProxyFileSystemTest, Basic) {

auto PWD = PFS.getCurrentWorkingDirectory();
ASSERT_FALSE(PWD.getError());
ASSERT_EQ("/", *PWD);
ASSERT_EQ("/", getPosixPath(*PWD));

SmallString<16> Path;
ASSERT_FALSE(PFS.getRealPath("a", Path));
ASSERT_EQ("/a", Path);
ASSERT_EQ("/a", getPosixPath(Path));

bool Local = true;
ASSERT_FALSE(PFS.isLocal("/a", Local));
Expand Down Expand Up @@ -1343,7 +1343,8 @@ TEST_F(InMemoryFileSystemTest, UniqueID) {
EXPECT_NE(FS.status("/a")->getUniqueID(), FS.status("/e")->getUniqueID());

// Recreating the "same" FS yields the same UniqueIDs.
vfs::InMemoryFileSystem FS2;
// Note: FS2 should match FS with respect to path normalization.
vfs::InMemoryFileSystem FS2(/*UseNormalizedPath=*/false);
ASSERT_TRUE(FS2.addFile("/a/b", 0, MemoryBuffer::getMemBuffer("text")));
EXPECT_EQ(FS.status("/a/b")->getUniqueID(),
FS2.status("/a/b")->getUniqueID());
Expand Down

0 comments on commit 35ab2a1

Please sign in to comment.