summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--llvm/include/llvm/Support/VirtualFileSystem.h27
-rw-r--r--llvm/lib/Support/VirtualFileSystem.cpp29
-rw-r--r--llvm/unittests/Support/VirtualFileSystemTest.cpp79
3 files changed, 115 insertions, 20 deletions
diff --git a/llvm/include/llvm/Support/VirtualFileSystem.h b/llvm/include/llvm/Support/VirtualFileSystem.h
index 7e1828062b3..282893e21dc 100644
--- a/llvm/include/llvm/Support/VirtualFileSystem.h
+++ b/llvm/include/llvm/Support/VirtualFileSystem.h
@@ -193,14 +193,22 @@ public:
class FileSystem;
+namespace detail {
+
+/// Keeps state for the recursive_directory_iterator.
+struct RecDirIterState {
+ std::stack<directory_iterator, std::vector<directory_iterator>> Stack;
+ bool HasNoPushRequest = false;
+};
+
+} // end namespace detail
+
/// An input iterator over the recursive contents of a virtual path,
/// similar to llvm::sys::fs::recursive_directory_iterator.
class recursive_directory_iterator {
- using IterState =
- std::stack<directory_iterator, std::vector<directory_iterator>>;
-
FileSystem *FS;
- std::shared_ptr<IterState> State; // Input iterator semantics on copy.
+ std::shared_ptr<detail::RecDirIterState>
+ State; // Input iterator semantics on copy.
public:
recursive_directory_iterator(FileSystem &FS, const Twine &Path,
@@ -212,8 +220,8 @@ public:
/// Equivalent to operator++, with an error code.
recursive_directory_iterator &increment(std::error_code &EC);
- const directory_entry &operator*() const { return *State->top(); }
- const directory_entry *operator->() const { return &*State->top(); }
+ const directory_entry &operator*() const { return *State->Stack.top(); }
+ const directory_entry *operator->() const { return &*State->Stack.top(); }
bool operator==(const recursive_directory_iterator &Other) const {
return State == Other.State; // identity
@@ -224,9 +232,12 @@ public:
/// Gets the current level. Starting path is at level 0.
int level() const {
- assert(!State->empty() && "Cannot get level without any iteration state");
- return State->size() - 1;
+ assert(!State->Stack.empty() &&
+ "Cannot get level without any iteration state");
+ return State->Stack.size() - 1;
}
+
+ void no_push() { State->HasNoPushRequest = true; }
};
/// The virtual file system interface.
diff --git a/llvm/lib/Support/VirtualFileSystem.cpp b/llvm/lib/Support/VirtualFileSystem.cpp
index 81ac5bbaa9c..9440eacaa89 100644
--- a/llvm/lib/Support/VirtualFileSystem.cpp
+++ b/llvm/lib/Support/VirtualFileSystem.cpp
@@ -2157,28 +2157,33 @@ vfs::recursive_directory_iterator::recursive_directory_iterator(
: FS(&FS_) {
directory_iterator I = FS->dir_begin(Path, EC);
if (I != directory_iterator()) {
- State = std::make_shared<IterState>();
- State->push(I);
+ State = std::make_shared<detail::RecDirIterState>();
+ State->Stack.push(I);
}
}
vfs::recursive_directory_iterator &
recursive_directory_iterator::increment(std::error_code &EC) {
- assert(FS && State && !State->empty() && "incrementing past end");
- assert(!State->top()->path().empty() && "non-canonical end iterator");
+ assert(FS && State && !State->Stack.empty() && "incrementing past end");
+ assert(!State->Stack.top()->path().empty() && "non-canonical end iterator");
vfs::directory_iterator End;
- if (State->top()->type() == sys::fs::file_type::directory_file) {
- vfs::directory_iterator I = FS->dir_begin(State->top()->path(), EC);
- if (I != End) {
- State->push(I);
- return *this;
+
+ if (State->HasNoPushRequest)
+ State->HasNoPushRequest = false;
+ else {
+ if (State->Stack.top()->type() == sys::fs::file_type::directory_file) {
+ vfs::directory_iterator I = FS->dir_begin(State->Stack.top()->path(), EC);
+ if (I != End) {
+ State->Stack.push(I);
+ return *this;
+ }
}
}
- while (!State->empty() && State->top().increment(EC) == End)
- State->pop();
+ while (!State->Stack.empty() && State->Stack.top().increment(EC) == End)
+ State->Stack.pop();
- if (State->empty())
+ if (State->Stack.empty())
State.reset(); // end iterator
return *this;
diff --git a/llvm/unittests/Support/VirtualFileSystemTest.cpp b/llvm/unittests/Support/VirtualFileSystemTest.cpp
index 992704c18fa..d5c01141bba 100644
--- a/llvm/unittests/Support/VirtualFileSystemTest.cpp
+++ b/llvm/unittests/Support/VirtualFileSystemTest.cpp
@@ -478,6 +478,85 @@ TEST(VirtualFileSystemTest, BasicRealFSRecursiveIteration) {
EXPECT_EQ(1, Counts[3]); // d
}
+TEST(VirtualFileSystemTest, BasicRealFSRecursiveIterationNoPush) {
+ ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
+
+ ScopedDir _a(TestDirectory + "/a");
+ ScopedDir _ab(TestDirectory + "/a/b");
+ ScopedDir _c(TestDirectory + "/c");
+ ScopedDir _cd(TestDirectory + "/c/d");
+ ScopedDir _e(TestDirectory + "/e");
+ ScopedDir _ef(TestDirectory + "/e/f");
+ ScopedDir _g(TestDirectory + "/g");
+
+ IntrusiveRefCntPtr<vfs::FileSystem> FS = vfs::getRealFileSystem();
+
+ // Test that calling no_push on entries without subdirectories has no effect.
+ {
+ std::error_code EC;
+ auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
+ ASSERT_FALSE(EC);
+
+ std::vector<std::string> Contents;
+ for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
+ I.increment(EC)) {
+ Contents.push_back(I->path());
+ char last = I->path().back();
+ switch (last) {
+ case 'b':
+ case 'd':
+ case 'f':
+ case 'g':
+ I.no_push();
+ break;
+ default:
+ break;
+ }
+ }
+ EXPECT_EQ(7U, Contents.size());
+ }
+
+ // Test that calling no_push skips subdirectories.
+ {
+ std::error_code EC;
+ auto I = vfs::recursive_directory_iterator(*FS, Twine(TestDirectory), EC);
+ ASSERT_FALSE(EC);
+
+ std::vector<std::string> Contents;
+ for (auto E = vfs::recursive_directory_iterator(); !EC && I != E;
+ I.increment(EC)) {
+ Contents.push_back(I->path());
+ char last = I->path().back();
+ switch (last) {
+ case 'a':
+ case 'c':
+ case 'e':
+ I.no_push();
+ break;
+ default:
+ break;
+ }
+ }
+
+ // Check contents, which may be in any order
+ EXPECT_EQ(4U, Contents.size());
+ int Counts[7] = {0, 0, 0, 0, 0, 0, 0};
+ for (const std::string &Name : Contents) {
+ ASSERT_FALSE(Name.empty());
+ int Index = Name[Name.size() - 1] - 'a';
+ ASSERT_TRUE(Index >= 0 && Index < 7);
+ Counts[Index]++;
+ }
+ EXPECT_EQ(1, Counts[0]); // a
+ EXPECT_EQ(0, Counts[1]); // b
+ EXPECT_EQ(1, Counts[2]); // c
+ EXPECT_EQ(0, Counts[3]); // d
+ EXPECT_EQ(1, Counts[4]); // e
+ EXPECT_EQ(0, Counts[5]); // f
+ EXPECT_EQ(1, Counts[6]); // g
+ }
+}
+
#ifdef LLVM_ON_UNIX
TEST(VirtualFileSystemTest, BrokenSymlinkRealFSRecursiveIteration) {
ScopedDir TestDirectory("virtual-file-system-test", /*Unique*/ true);
OpenPOWER on IntegriCloud