@@ -129,11 +129,13 @@ impl WorkspaceFilter {
129129 /// opened as `/var/folders/...` or through any symlinked directory is
130130 /// reported by FSEvents at its resolved location, `/private/var/folders/...`,
131131 /// so a plain prefix test rejected every event and the watcher admitted
132- /// nothing at all. Both sides are compared resolved and as given.
132+ /// nothing at all. The event path is compared, as reported, against both
133+ /// spellings of the root; that needs no filesystem access, so it still
134+ /// works when the file's directory was deleted along with it.
133135 ///
134- /// The event path is resolved through its parent directory, which still
135- /// exists when the file itself was just deleted . The most specific root
136- /// wins when workspace folders nest.
136+ /// A path reported in unresolved form is additionally resolved through its
137+ /// parent directory . The most specific root wins when workspace folders
138+ /// nest.
137139 fn locate ( & self , path : & Path ) -> Option < ( PathBuf , PathBuf , & IndexConfig , & globset:: GlobSet ) > {
138140 let resolved = path
139141 . parent ( )
@@ -143,7 +145,11 @@ impl WorkspaceFilter {
143145
144146 let mut best: Option < ( usize , PathBuf , PathBuf , & IndexConfig , & globset:: GlobSet ) > = None ;
145147 for ( given, canonical, config, exclude_set) in & self . roots {
146- for ( candidate, root) in [ ( Some ( path) , given) , ( resolved. as_deref ( ) , canonical) ] {
148+ for ( candidate, root) in [
149+ ( Some ( path) , given) ,
150+ ( Some ( path) , canonical) ,
151+ ( resolved. as_deref ( ) , canonical) ,
152+ ] {
147153 let Some ( candidate) = candidate else { continue } ;
148154 let Ok ( relative) = candidate. strip_prefix ( root) else {
149155 continue ;
@@ -867,6 +873,26 @@ mod tests {
867873 ) ;
868874 }
869875
876+ /// Deleting a directory removes the parent an event path would be resolved
877+ /// through, so a resolved-form path must be located without touching disk.
878+ #[ cfg( unix) ]
879+ #[ test]
880+ fn workspace_filter_locates_files_in_a_deleted_directory ( ) {
881+ let tmp = tempfile:: tempdir ( ) . unwrap ( ) ;
882+ let real = tmp. path ( ) . join ( "real" ) ;
883+ std:: fs:: create_dir_all ( real. join ( "src/old" ) ) . unwrap ( ) ;
884+ std:: fs:: write ( real. join ( "src/old/a.rs" ) , "fn a() {}\n " ) . unwrap ( ) ;
885+ let link = tmp. path ( ) . join ( "link" ) ;
886+ std:: os:: unix:: fs:: symlink ( & real, & link) . unwrap ( ) ;
887+
888+ let filter = WorkspaceFilter :: new ( std:: slice:: from_ref ( & link) , & filter_config ( & [ ] , 20 ) ) ;
889+ let deleted = real. canonicalize ( ) . unwrap ( ) . join ( "src/old/a.rs" ) ;
890+ std:: fs:: remove_dir_all ( real. join ( "src/old" ) ) . unwrap ( ) ;
891+
892+ assert ! ( filter. admits( & deleted) ) ;
893+ assert_eq ! ( filter. indexed_form( & deleted) , link. join( "src/old/a.rs" ) ) ;
894+ }
895+
870896 #[ test]
871897 fn workspace_filter_prefers_the_most_specific_root ( ) {
872898 let tmp = tempfile:: tempdir ( ) . unwrap ( ) ;
0 commit comments