diff --git a/crates/erg_common/config.rs b/crates/erg_common/config.rs index 6919a32f..9752066a 100644 --- a/crates/erg_common/config.rs +++ b/crates/erg_common/config.rs @@ -441,16 +441,16 @@ impl Input { } /// resolution order: - /// 1. `{path}.er` - /// 2. `{path}/__init__.er` + /// 1. `{path/to}.er` + /// 2. `{path/to}/__init__.er` fn resolve_local(&self, path: &Path) -> Result { let mut dir = self.dir(); dir.push(path); - dir.set_extension("er"); // {path}.er + dir.set_extension("er"); // {path/to}.er let path = dir.canonicalize().or_else(|_| { - dir.pop(); - dir.push(path); - dir.push("__init__.er"); // -> {path}/__init__.er + dir.pop(); // {path} + dir.push(path.iter().last().unwrap_or_default()); // {path/to} + dir.push("__init__.er"); // -> {path/to}/__init__.er dir.canonicalize() })?; Ok(normalize_path(path)) @@ -464,10 +464,10 @@ impl Input { } /// resolution order: - /// 1. `{path}.d.er` - /// 2. `{path}/__init__.d.er` - /// 3. `__pycache__/{path}.d.er` - /// 4. `{path}/__pycache__/__init__.d.er` + /// 1. `{path/to}.d.er` + /// 2. `{path/to}/__init__.d.er` + /// 3. `{path}/__pycache__/{to}.d.er` + /// 4. `{path/to}/__pycache__/__init__.d.er` fn _resolve_local_decl(&self, path: &Path) -> Result { let mut dir = self.dir(); let mut comps = path.components(); @@ -477,29 +477,29 @@ impl Input { let last_path = Path::new(&last); dir.push(comps); dir.push(last_path); - dir.set_extension("d.er"); // {path}.d.er + dir.set_extension("d.er"); // {path/to}.d.er let path = dir .canonicalize() .or_else(|_| { - dir.pop(); // {path}.d.er -> ./ - dir.push(last_path); // -> {path} - dir.push("__init__.d.er"); // -> {path}/__init__.d.er + dir.pop(); // {path/to}.d.er -> {path} + dir.push(last_path); // -> {path/to} + dir.push("__init__.d.er"); // -> {path/to}/__init__.d.er dir.canonicalize() }) .or_else(|_| { + dir.pop(); // -> {path/to} dir.pop(); // -> {path} - dir.pop(); // -> ./ - dir.push("__pycache__"); - dir.push(last_path); - dir.set_extension("d.er"); // -> __pycache__/{path}.d.er + dir.push("__pycache__"); // -> {path}/__pycache__ + dir.push(last_path); // -> {path}/__pycache__/{to} + dir.set_extension("d.er"); // -> {path}/__pycache__/{to}.d.er dir.canonicalize() }) .or_else(|_| { - dir.pop(); // -> __pycache__ - dir.pop(); // -> ./ - dir.push(last_path); // -> {path} - dir.push("__pycache__"); // -> {path}/__pycache__ - dir.push("__init__.d.er"); // -> {path}/__pycache__/__init__.d.er + dir.pop(); // -> {path}/__pycache__ + dir.pop(); // -> {path} + dir.push(last_path); // -> {path/to} + dir.push("__pycache__"); // -> {path/to}/__pycache__ + dir.push("__init__.d.er"); // -> {path/to}/__pycache__/__init__.d.er dir.canonicalize() })?; Ok(normalize_path(path)) @@ -551,10 +551,10 @@ impl Input { } /// resolution order: - /// 1. `./{path}.er` - /// 2. `./{path}/__init__.er` - /// 3. `std/{path}.er` - /// 4. `std/{path}/__init__.er` + /// 1. `./{path/to}.er` + /// 2. `./{path/to}/__init__.er` + /// 3. `std/{path/to}.er` + /// 4. `std/{path/to}/__init__.er` pub fn resolve_real_path(&self, path: &Path) -> Option { if let Ok(path) = self.resolve_local(path) { Some(path) @@ -575,16 +575,16 @@ impl Input { } /// resolution order: - /// 1. `{path}.d.er` - /// 2. `{path}/__init__.d.er` - /// 3. `__pycache__/{path}.d.er` - /// 4. `{path}/__pycache__/__init__.d.er` - /// 5. `{path}.d/__init__.d.er` - /// 6. `{path}.d/__pycache__/__init__.d.er` - /// 7. `std/{path}.d.er` - /// 8. `std/{path}/__init__.d.er` - /// 9. `site-packages/__pycache__/{path}.d.er` - /// 10. `site-packages/{path}/__pycache__/__init__.d.er` + /// 1. `{path/to}.d.er` + /// 2. `{path/to}/__init__.d.er` + /// 3. `{path}/__pycache__/{to}.d.er` + /// 4. `{path/to}/__pycache__/__init__.d.er` + /// 5. `{path.d/to.d}/__init__.d.er` + /// 6. `{path.d/to.d}/__pycache__/__init__.d.er` + /// 7. `std/{path/to}.d.er` + /// 8. `std/{path/to}/__init__.d.er` + /// 9. `site-packages/{path}/__pycache__/{to}.d.er` + /// 10. `site-packages/{path/to}/__pycache__/__init__.d.er` pub fn resolve_decl_path(&self, path: &Path) -> Option { if let Ok(path) = self.resolve_local_decl(path) { Some(path) diff --git a/crates/erg_compiler/context/register.rs b/crates/erg_compiler/context/register.rs index 29bf46e0..98d8fec9 100644 --- a/crates/erg_compiler/context/register.rs +++ b/crates/erg_compiler/context/register.rs @@ -1793,12 +1793,12 @@ impl Context { } } - fn import_err(&self, __name__: &Str, loc: &impl Locational) -> TyCheckErrors { + fn import_err(&self, line: u32, __name__: &Str, loc: &impl Locational) -> TyCheckErrors { let mod_cache = self.mod_cache(); let py_mod_cache = self.py_mod_cache(); TyCheckErrors::from(TyCheckError::import_error( self.cfg.input.clone(), - line!() as usize, + line as usize, format!("module {__name__} not found"), loc.loc(), self.caused_by(), @@ -1813,7 +1813,7 @@ impl Context { let path = match self.cfg.input.resolve_real_path(Path::new(&__name__[..])) { Some(path) => path, None => { - return Err(self.import_err(__name__, loc)); + return Err(self.import_err(line!(), __name__, loc)); } }; self.check_mod_vis(path.as_path(), __name__, loc)?; @@ -1834,8 +1834,14 @@ impl Context { __name__: &Str, loc: &impl Locational, ) -> CompileResult<()> { - if let Some(parent) = path.parent() { - if FileKind::from(path).is_simple_erg_file() && DirKind::from(parent).is_erg_module() { + let file_kind = FileKind::from(path); + let parent = if file_kind.is_init_er() { + path.parent().and_then(|p| p.parent()) + } else { + path.parent() + }; + if let Some(parent) = parent { + if DirKind::from(parent).is_erg_module() { let parent = parent.join("__init__.er"); let parent_module = if let Some(parent) = self.get_mod_with_path(&parent) { Some(parent) @@ -1845,10 +1851,10 @@ impl Context { self.get_mod_with_path(&parent) }; if let Some(parent_module) = parent_module { - let import_err = || { + let import_err = |line| { TyCheckErrors::from(TyCheckError::import_error( self.cfg.input.clone(), - line!() as usize, + line as usize, format!("module `{__name__}` is not public"), loc.loc(), self.caused_by(), @@ -1856,13 +1862,18 @@ impl Context { None, )) }; - let mod_name = path.file_stem().unwrap_or_default().to_string_lossy(); + let file_stem = if file_kind.is_init_er() { + path.parent().unwrap().file_stem() + } else { + path.file_stem() + }; + let mod_name = file_stem.unwrap_or_default().to_string_lossy(); if let Some((_, vi)) = parent_module.get_var_info(&mod_name) { if !vi.vis.compatible(&ast::AccessModifier::Public, self) { - return Err(import_err()); + return Err(import_err(line!())); } } else { - return Err(import_err()); + return Err(import_err(line!())); } } } @@ -1881,7 +1892,7 @@ impl Context { let src = cfg .input .try_read() - .map_err(|_| self.import_err(__name__, loc))?; + .map_err(|_| self.import_err(line!(), __name__, loc))?; let mut builder = HIRBuilder::new_with_cache(cfg, __name__, self.shared.as_ref().unwrap().clone()); match builder.build(src, "exec") { @@ -2040,7 +2051,7 @@ impl Context { let src = cfg .input .try_read() - .map_err(|_| self.import_err(__name__, loc))?; + .map_err(|_| self.import_err(line!(), __name__, loc))?; let mut builder = HIRBuilder::new_with_cache( cfg, self.mod_name(&path), diff --git a/tests/should_err/foo/__init__.er b/tests/should_err/foo/__init__.er index 07075e11..d51d4656 100644 --- a/tests/should_err/foo/__init__.er +++ b/tests/should_err/foo/__init__.er @@ -1 +1,2 @@ .bar = import "./bar" +.quux = import "./quux" diff --git a/tests/should_err/foo/quux/__init__.er b/tests/should_err/foo/quux/__init__.er new file mode 100644 index 00000000..e69de29b diff --git a/tests/should_err/import.er b/tests/should_err/import.er index 5598bbe1..66d43cf8 100644 --- a/tests/should_err/import.er +++ b/tests/should_err/import.er @@ -2,3 +2,4 @@ _ = import "foo" # OK _ = import "foo/bar" # OK _ = import "foo/baz" # ERR _ = import "foo/qux" # ERR +_ = import "foo/quux" # OK