From b8098c131192c03eb41c359c7eb9b1877470c6b3 Mon Sep 17 00:00:00 2001 From: Martin Vidner Date: Fri, 31 Oct 2025 16:41:06 +0100 Subject: [PATCH 1/2] fix finding modules Without this fix, `xtr` would not find the nested `web` modules, mistakenly finding the top-level `web` instead. ```console $ find src -type f src/main.rs src/software.rs src/software/web.rs src/storage.rs src/storage/web.rs src/web.rs ``` --- xtr/src/crate_visitor.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/xtr/src/crate_visitor.rs b/xtr/src/crate_visitor.rs index 1e61fb7..4f77a04 100644 --- a/xtr/src/crate_visitor.rs +++ b/xtr/src/crate_visitor.rs @@ -124,12 +124,6 @@ where .parse_mod(subdir) .unwrap_or_else(|err| self.mod_error = Some(err)); } - let adjacent = self.mod_dir.join(format!("{}.rs", mod_name)); - if adjacent.is_file() { - return self - .parse_mod(adjacent) - .unwrap_or_else(|err| self.mod_error = Some(err)); - } let mut nested_mod_dir = self.current_path.clone(); nested_mod_dir.pop(); @@ -153,6 +147,13 @@ where } } + let adjacent = self.mod_dir.join(format!("{}.rs", mod_name)); + if adjacent.is_file() { + return self + .parse_mod(adjacent) + .unwrap_or_else(|err| self.mod_error = Some(err)); + } + panic!( "No file with module definition for `mod {}` in file {:?}", mod_name, self.current_path From 75a24600b572194a9303eb8c11b2924bca333895 Mon Sep 17 00:00:00 2001 From: Martin Vidner Date: Mon, 10 Nov 2025 11:46:54 +0100 Subject: [PATCH 2/2] proper module finding, distinguish the root case In current.rs, "mod foo" can be found in: 1. ./current/foo.rs 2. ./current/foo/mod.rs but if current.rs is in fact lib.rs or main.rs, then in: 3. ./foo.rs 4. ./foo/mod.rs Therefore, let visit_item_mod try only half of the files, depending on whether the current file is the module root. --- xtr/src/crate_visitor.rs | 85 ++++++++++++++++++++++++++-------------- 1 file changed, 55 insertions(+), 30 deletions(-) diff --git a/xtr/src/crate_visitor.rs b/xtr/src/crate_visitor.rs index 4f77a04..ffdd955 100644 --- a/xtr/src/crate_visitor.rs +++ b/xtr/src/crate_visitor.rs @@ -32,15 +32,17 @@ where V: FnMut(&PathBuf, &str, &syn::File) -> Result<(), Error>, { let mut parser = Parser { + is_root: bool::default(), current_path: PathBuf::default(), mod_dir: PathBuf::default(), mod_error: None, mod_visitor: visitor, }; - parser.parse_mod(crate_root) + parser.parse_mod(crate_root, true) } struct Parser { + is_root: bool, // Parsing lib.rs or main.rs current_path: PathBuf, // The current file being parsed mod_dir: PathBuf, mod_error: Option, // An error occuring while visiting the modules @@ -51,16 +53,18 @@ impl Parser where ModVisitor: FnMut(&PathBuf, &str, &syn::File) -> Result<(), Error>, { - fn parse_mod>(&mut self, mod_path: P) -> Result<(), Error> { + fn parse_mod>(&mut self, mod_path: P, is_root: bool) -> Result<(), Error> { let mut s = String::new(); let mut f = File::open(&mod_path)?; f.read_to_string(&mut s)?; let fi = syn::parse_file(&s)?; + let mut is_root = is_root; let mut current_path = mod_path.as_ref().into(); let mut mod_dir = mod_path.as_ref().parent().unwrap().into(); + swap(&mut self.is_root, &mut is_root); swap(&mut self.current_path, &mut current_path); swap(&mut self.mod_dir, &mut mod_dir); @@ -71,6 +75,7 @@ where (self.mod_visitor)(&self.current_path, &s, &fi)?; + swap(&mut self.is_root, &mut is_root); swap(&mut self.current_path, &mut current_path); swap(&mut self.mod_dir, &mut mod_dir); @@ -87,6 +92,8 @@ where return; } + // Example: + // mod foo { ... } if item.content.is_some() { let mut parent = self.mod_dir.join(item.ident.to_string()); swap(&mut self.mod_dir, &mut parent); @@ -96,6 +103,9 @@ where } // Determine the path of the inner module's file + // Example: + // #[path = "tls.rs"] + // mod ssl; for attr in &item.attrs { match &attr.meta { syn::Meta::NameValue(syn::MetaNameValue { @@ -109,49 +119,64 @@ where }) if path.is_ident("path") => { let mod_path = self.mod_dir.join(s.value()); return self - .parse_mod(mod_path) + .parse_mod(mod_path, self.is_root) .unwrap_or_else(|err| self.mod_error = Some(err)); } _ => {} } } - let mod_name = item.ident.unraw().to_string(); - let mut subdir = self.mod_dir.join(mod_name.clone()); - subdir.push("mod.rs"); - if subdir.is_file() { - return self - .parse_mod(subdir) - .unwrap_or_else(|err| self.mod_error = Some(err)); - } + let mod_name = item.ident.unraw().to_string(); // unraw("r#move") = "move" + + // In current.rs, "mod foo" can be found in: + // 1. ./current/foo.rs + // 2. ./current/foo/mod.rs + // but if current.rs is in fact lib.rs or main.rs, then in: + // 3. ./foo.rs + // 4. ./foo/mod.rs - let mut nested_mod_dir = self.current_path.clone(); - nested_mod_dir.pop(); - let subdir = self.current_path.file_name().unwrap().to_str().unwrap(); - if let Some(without_suffix) = subdir.strip_suffix(".rs") { - // Support the case when "mod bar;" in src/foo.rs means an src/foo/bar.rs file. - let adjacent = nested_mod_dir.join(format!("{}/{}.rs", without_suffix, mod_name)); + if self.is_root { + // 3. ./foo.rs + let adjacent = self.mod_dir.join(format!("{}.rs", mod_name)); if adjacent.is_file() { return self - .parse_mod(adjacent) + .parse_mod(adjacent, false) .unwrap_or_else(|err| self.mod_error = Some(err)); } - let adjacent_mod = nested_mod_dir - .join(without_suffix) - .join(&mod_name) - .join("mod.rs"); - if adjacent_mod.is_file() { + + // 4. ./foo/mod.rs + let mut subdir = self.mod_dir.join(mod_name.clone()); + subdir.push("mod.rs"); + if subdir.is_file() { return self - .parse_mod(adjacent_mod) + .parse_mod(subdir, false) .unwrap_or_else(|err| self.mod_error = Some(err)); } - } + } else { + let mut nested_mod_dir = self.current_path.clone(); + nested_mod_dir.pop(); + let subdir = self.current_path.file_name().unwrap().to_str().unwrap(); + if let Some(without_suffix) = subdir.strip_suffix(".rs") { + // 1. ./current/foo.rs + // Support the case when "mod bar;" in src/foo.rs means an src/foo/bar.rs file. + let adjacent = nested_mod_dir.join(format!("{}/{}.rs", without_suffix, mod_name)); + if adjacent.is_file() { + return self + .parse_mod(adjacent, false) + .unwrap_or_else(|err| self.mod_error = Some(err)); + } - let adjacent = self.mod_dir.join(format!("{}.rs", mod_name)); - if adjacent.is_file() { - return self - .parse_mod(adjacent) - .unwrap_or_else(|err| self.mod_error = Some(err)); + // 2. ./current/foo/mod.rs + let adjacent_mod = nested_mod_dir + .join(without_suffix) + .join(&mod_name) + .join("mod.rs"); + if adjacent_mod.is_file() { + return self + .parse_mod(adjacent_mod, false) + .unwrap_or_else(|err| self.mod_error = Some(err)); + } + } } panic!(