@@ -141,33 +141,44 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath &
141
141
for (auto & entry : DirectoryIterator{makeAbsPath (path)}) {
142
142
checkInterrupt ();
143
143
auto type = [&]() -> std::optional<Type> {
144
- std::filesystem::file_type nativeType;
145
144
try {
146
- nativeType = entry.symlink_status ().type ();
145
+ /* WARNING: We are specifically not calling symlink_status()
146
+ * here, because that always translates to `stat` call and
147
+ * doesn't make use of any caching. Instead, we have to
148
+ * rely on the myriad of `is_*` functions, which actually do
149
+ * the caching. If you are in doubt then take a look at the
150
+ * libstdc++ implementation [1] and the standard proposal
151
+ * about the caching variations of directory_entry [2].
152
+
153
+ * [1]: https://github.com/gcc-mirror/gcc/blob/8ea555b7b4725dbc5d9286f729166cd54ce5b615/libstdc%2B%2B-v3/include/bits/fs_dir.h#L341-L348
154
+ * [2]: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0317r1.html
155
+ */
156
+
157
+ /* Check for symlink first, because other getters follow symlinks. */
158
+ if (entry.is_symlink ())
159
+ return tSymlink;
160
+ if (entry.is_regular_file ())
161
+ return tRegular;
162
+ if (entry.is_directory ())
163
+ return tDirectory;
164
+ if (entry.is_character_file ())
165
+ return tChar;
166
+ if (entry.is_block_file ())
167
+ return tBlock;
168
+ if (entry.is_fifo ())
169
+ return tFifo;
170
+ if (entry.is_socket ())
171
+ return tSocket;
172
+ return tUnknown;
147
173
} catch (std::filesystem::filesystem_error & e) {
148
174
// We cannot always stat the child. (Ideally there is no
149
175
// stat because the native directory entry has the type
150
176
// already, but this isn't always the case.)
151
177
if (e.code () == std::errc::permission_denied || e.code () == std::errc::operation_not_permitted)
152
178
return std::nullopt;
153
- else throw ;
179
+ else
180
+ throw ;
154
181
}
155
-
156
- // cannot exhaustively enumerate because implementation-specific
157
- // additional file types are allowed.
158
- #pragma GCC diagnostic push
159
- #pragma GCC diagnostic ignored "-Wswitch-enum"
160
- switch (nativeType) {
161
- case std::filesystem::file_type::regular: return Type::tRegular; break ;
162
- case std::filesystem::file_type::symlink: return Type::tSymlink; break ;
163
- case std::filesystem::file_type::directory: return Type::tDirectory; break ;
164
- case std::filesystem::file_type::character: return Type::tChar; break ;
165
- case std::filesystem::file_type::block: return Type::tBlock; break ;
166
- case std::filesystem::file_type::fifo: return Type::tFifo; break ;
167
- case std::filesystem::file_type::socket: return Type::tSocket; break ;
168
- default : return tUnknown;
169
- }
170
- #pragma GCC diagnostic pop
171
182
}();
172
183
res.emplace (entry.path ().filename ().string (), type);
173
184
}
0 commit comments