mirror of
https://github.com/erg-lang/erg.git
synced 2025-08-04 10:49:54 +00:00
fix: nested module resolution bug
This commit is contained in:
parent
83aae24317
commit
ded10fc3d9
7 changed files with 51 additions and 24 deletions
|
@ -149,7 +149,7 @@ pub enum ResolveError {
|
|||
},
|
||||
}
|
||||
|
||||
pub type ResolveResult<T> = Result<T, ResolveError>;
|
||||
pub type ResolveResult<T> = Result<T, Vec<ResolveError>>;
|
||||
|
||||
/// Resolve dependencies and build a package.
|
||||
/// This object should be a singleton.
|
||||
|
@ -387,7 +387,8 @@ impl<ASTBuilder: ASTBuildable, HIRBuilder: Buildable>
|
|||
) -> Result<CompleteArtifact, IncompleteArtifact> {
|
||||
let cfg = self.cfg.copy();
|
||||
log!(info "Start dependency resolution process");
|
||||
let _ = self.resolve(&mut ast, &cfg);
|
||||
let res = self.resolve(&mut ast, &cfg);
|
||||
debug_assert!(res.is_ok(), "{:?}", res.unwrap_err());
|
||||
log!(info "Dependency resolution process completed");
|
||||
log!("graph:\n{}", self.shared.graph.display());
|
||||
if self.parse_errors.errors.is_empty() {
|
||||
|
@ -411,47 +412,55 @@ impl<ASTBuilder: ASTBuildable, HIRBuilder: Buildable>
|
|||
/// Analyze ASTs and make the dependencies graph.
|
||||
/// If circular dependencies are found, inline submodules to eliminate the circularity.
|
||||
fn resolve(&mut self, ast: &mut AST, cfg: &ErgConfig) -> ResolveResult<()> {
|
||||
let mut result = Ok(());
|
||||
let mut errs = vec![];
|
||||
for chunk in ast.module.iter_mut() {
|
||||
if let Err(err) = self.check_import(chunk, cfg) {
|
||||
result = Err(err);
|
||||
errs.extend(err);
|
||||
}
|
||||
}
|
||||
result
|
||||
if errs.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errs)
|
||||
}
|
||||
}
|
||||
|
||||
fn check_import(&mut self, expr: &mut Expr, cfg: &ErgConfig) -> ResolveResult<()> {
|
||||
let mut result = Ok(());
|
||||
let mut errs = vec![];
|
||||
match expr {
|
||||
Expr::Call(call) if call.additional_operation().is_some_and(|op| op.is_import()) => {
|
||||
if let Err(err) = self.register(expr, cfg) {
|
||||
result = Err(err);
|
||||
errs.extend(err);
|
||||
}
|
||||
}
|
||||
Expr::Def(def) => {
|
||||
for expr in def.body.block.iter_mut() {
|
||||
if let Err(err) = self.check_import(expr, cfg) {
|
||||
result = Err(err);
|
||||
errs.extend(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::Dummy(chunks) => {
|
||||
for chunk in chunks.iter_mut() {
|
||||
if let Err(err) = self.check_import(chunk, cfg) {
|
||||
result = Err(err);
|
||||
errs.extend(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::Compound(chunks) => {
|
||||
for chunk in chunks.iter_mut() {
|
||||
if let Err(err) = self.check_import(chunk, cfg) {
|
||||
result = Err(err);
|
||||
errs.extend(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
result
|
||||
if errs.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errs)
|
||||
}
|
||||
}
|
||||
|
||||
fn analysis_in_progress(path: &Path) -> bool {
|
||||
|
@ -588,10 +597,10 @@ impl<ASTBuilder: ASTBuildable, HIRBuilder: Buildable>
|
|||
.is_err()
|
||||
{
|
||||
self.submodules.push(from_path.clone());
|
||||
return Err(ResolveError::CycleDetected {
|
||||
return Err(vec![ResolveError::CycleDetected {
|
||||
path: import_path,
|
||||
submod_input: cfg.input.clone(),
|
||||
});
|
||||
}]);
|
||||
}
|
||||
if import_path == from_path
|
||||
|| self.submodules.contains(&import_path)
|
||||
|
@ -627,19 +636,20 @@ impl<ASTBuilder: ASTBuildable, HIRBuilder: Buildable>
|
|||
}
|
||||
}
|
||||
};
|
||||
if let Err(ResolveError::CycleDetected { path, submod_input }) =
|
||||
self.resolve(&mut ast, &import_cfg)
|
||||
{
|
||||
if let Err(mut errs) = self.resolve(&mut ast, &import_cfg) {
|
||||
*expr = Expr::InlineModule(InlineModule::new(
|
||||
Input::file(import_path.to_path_buf()),
|
||||
ast,
|
||||
call.clone(),
|
||||
));
|
||||
if path != from_path {
|
||||
return Err(ResolveError::CycleDetected { path, submod_input });
|
||||
} else {
|
||||
self.cyclic.push(path);
|
||||
errs.retain(|err| match err {
|
||||
ResolveError::CycleDetected { path, .. } => path != &from_path,
|
||||
});
|
||||
if errs.is_empty() {
|
||||
self.cyclic.push(from_path);
|
||||
return Ok(());
|
||||
} else {
|
||||
return Err(errs);
|
||||
}
|
||||
}
|
||||
let prev = self.asts.insert(import_path, (__name__.clone(), ast));
|
||||
|
|
|
@ -51,10 +51,12 @@ pub enum SubstituteResult {
|
|||
}
|
||||
|
||||
impl Context {
|
||||
/// Returns `true` if the module is being inspected, or has been inspected
|
||||
pub(crate) fn mod_registered(&self, path: &NormalizedPathBuf) -> bool {
|
||||
(self.shared.is_some() && self.promises().is_registered(path)) || self.mod_cached(path)
|
||||
}
|
||||
|
||||
/// Returns `true` if the module has been inspected
|
||||
pub(crate) fn mod_cached(&self, path: &Path) -> bool {
|
||||
self.mod_cache().get(path).is_some() || self.py_mod_cache().get(path).is_some()
|
||||
}
|
||||
|
|
|
@ -415,7 +415,9 @@ impl From<&Def> for ContextKind {
|
|||
DefKind::Class | DefKind::Inherit => Self::Class,
|
||||
DefKind::Trait | DefKind::Subsume => Self::Trait,
|
||||
DefKind::StructuralTrait => Self::StructuralTrait,
|
||||
DefKind::ErgImport | DefKind::PyImport | DefKind::RsImport => Self::Module,
|
||||
DefKind::ErgImport | DefKind::PyImport | DefKind::RsImport | DefKind::InlineModule => {
|
||||
Self::Module
|
||||
}
|
||||
DefKind::Other => {
|
||||
if def.is_subr() {
|
||||
if def.sig.ident().unwrap().is_procedural() {
|
||||
|
|
|
@ -10,7 +10,7 @@ use erg_common::dict;
|
|||
use erg_common::dict::Dict;
|
||||
use erg_common::error::{Location, MultiErrorDisplay};
|
||||
use erg_common::fresh::FreshNameGenerator;
|
||||
use erg_common::pathutil::mod_name;
|
||||
use erg_common::pathutil::{mod_name, NormalizedPathBuf};
|
||||
use erg_common::set;
|
||||
use erg_common::set::Set;
|
||||
use erg_common::traits::{ExitStatus, Locational, NoTypeDisplay, Runnable, Stream};
|
||||
|
@ -2981,7 +2981,10 @@ impl<A: ASTBuildable> GenericASTLowerer<A> {
|
|||
expect: Option<&Type>,
|
||||
) -> hir::Call {
|
||||
log!(info "entered {}", fn_name!());
|
||||
let path = inline.input.path().to_path_buf();
|
||||
let path = NormalizedPathBuf::from(inline.input.path().to_path_buf());
|
||||
if self.module.context.mod_cached(&path) {
|
||||
return self.lower_call(inline.import, expect);
|
||||
}
|
||||
let parent = self.get_mod_ctx().context.get_module().unwrap().clone();
|
||||
let mod_ctx = ModuleContext::new(parent, dict! {});
|
||||
let mod_name = mod_name(&path);
|
||||
|
|
|
@ -277,6 +277,10 @@ impl SharedModuleGraph {
|
|||
self.0.borrow_mut().sort()
|
||||
}
|
||||
|
||||
pub fn entries(&self) -> Set<NormalizedPathBuf> {
|
||||
self.0.borrow().iter().map(|n| n.id.clone()).collect()
|
||||
}
|
||||
|
||||
pub fn initialize(&self) {
|
||||
self.0.borrow_mut().initialize();
|
||||
}
|
||||
|
|
|
@ -1236,7 +1236,7 @@ impl LimitedDisplay for Type {
|
|||
}
|
||||
Self::Poly { name, params } => {
|
||||
write!(f, "{name}(")?;
|
||||
if self.is_module() {
|
||||
if !DEBUG_MODE && self.is_module() {
|
||||
// Module("path/to/module.er") -> Module("module.er")
|
||||
let name = params.first().unwrap().to_string_unabbreviated();
|
||||
let name = name.replace("__init__.d.er", "").replace("__init__.er", "");
|
||||
|
|
|
@ -5522,6 +5522,7 @@ pub enum DefKind {
|
|||
PyImport,
|
||||
RsImport,
|
||||
Patch,
|
||||
InlineModule,
|
||||
/// type alias included
|
||||
Other,
|
||||
}
|
||||
|
@ -5556,6 +5557,10 @@ impl DefKind {
|
|||
self.is_erg_import() || self.is_py_import() || self.is_rs_import()
|
||||
}
|
||||
|
||||
pub fn is_inline_module(&self) -> bool {
|
||||
matches!(self, Self::InlineModule)
|
||||
}
|
||||
|
||||
pub const fn is_other(&self) -> bool {
|
||||
matches!(self, Self::Other)
|
||||
}
|
||||
|
@ -5607,6 +5612,7 @@ impl DefBody {
|
|||
Some("rsimport") => DefKind::RsImport,
|
||||
_ => DefKind::Other,
|
||||
},
|
||||
Expr::InlineModule(_) => DefKind::InlineModule,
|
||||
_ => DefKind::Other,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue