Shift Module Resolution to AST and Introduce Scope Analysis#345
Conversation
3386db6 to
415a69d
Compare
| let source = SourceFile::anonymous(file.clone()); | ||
| let mut error_handler = ErrorCollector::new(); | ||
| let parse_program = parse::Program::parse_from_str_with_errors(source, &mut error_handler); | ||
| dbg!(&file); |
There was a problem hiding this comment.
Oh, I forgot about that. I delete it during the refactoring
| @@ -13,36 +13,30 @@ | |||
| //! to build a Directed Acyclic Graph (DAG) of the project's dependencies. Because | |||
| //! the final AST requires a flat array of items, the driver applies a deterministic | |||
There was a problem hiding this comment.
because import cache is keyed only by UseDecl, identical use crate::... in different package roots can rewrite to the wrong file
There was a problem hiding this comment.
Fixed and added more test cases
e81b73e to
ce3f943
Compare
|
@apoelstra, I’d like to get your thoughts on our commit and PR structure. I'm trying my best to follow the CONTRIBUTING.md rules for atomic PRs, but for massive, tightly coupled features like the current Modules (or recent imports in PR #264), it’s becoming a huge bottleneck. My current workflow is: brainstorm the architecture -> implement the entire feature on a single branch -> painstakingly divide it into separate PRs. I have to do it this way because I frequently discover bugs in my earlier assumptions only after I've written the later commits. However, this means if a reviewer catches an issue in PR 3 that requires a change in the code from PR 1, keeping the commit history clean and linear becomes incredibly difficult. I feel like I am spending 50% of my time just organizing Git history instead of building the new features. Is there a better or accepted way to handle these large, monolithic architectural changes? Should we be relaxing the commit rules for these specific cases? Let me know what you think! |
| dbg!(curr_id); | ||
| dbg!(&queue); |
| Self::parse_and_get_source_module(&path, current.source, import_span, ctx.handler) | ||
| else { | ||
| ctx.invalid_imports.push(path); | ||
| ctx.invalid_imports.insert(path); |
There was a problem hiding this comment.
| ctx.invalid_imports.insert(path); | |
| let _ = ctx.invalid_imports.insert(path); |
There was a problem hiding this comment.
It checked before in .contains method. Added comment for this
| /// through every recursive call. Lives only for the duration of [`resolve_imports`]. | ||
| struct ImportContext<'a> { | ||
| importer_source: &'a CanonSourceFile, | ||
| current: &'a CurrentModule<'a>, |
There was a problem hiding this comment.
Is this lifetime really needed?
| // might resolve to completely different implementations of the `foo` function. | ||
| let mut use_decl = use_decl.clone(); | ||
| use_decl.set_file_id(self.current.id); | ||
|
|
There was a problem hiding this comment.
nit: you can create span here to prevent second clone
| let mut use_decl = use_decl.clone(); | ||
| use_decl.set_file_id(self.current.id); | ||
|
|
||
| let result = (resolved.path.clone(), *use_decl.span()); |
There was a problem hiding this comment.
| let result = (resolved.path.clone(), *use_decl.span()); | |
| let result: <add explicit type> = (resolved.path.clone(), *use_decl.span()); |
| parse::Item::Module(_) | parse::Item::Use(_) | parse::Item::Ignored => continue, | ||
| } | ||
| items.push(new_elem); | ||
| /* |
There was a problem hiding this comment.
I have already deleted this and the other unnecessary comments
|
|
||
| let name = item.name(); | ||
| let local_id = (name.clone(), source_id); | ||
| //if target_id != MAIN_MODULE { |
| /// * May also return errors propagated from item collection and insertion, such as [`Error::PrivateItem`] or [`Error::RedefinedItem`]. | ||
| pub fn resolve_use(&mut self, use_decl: &UseDecl) -> Result<(), Error> { | ||
| let path = use_decl.path(); | ||
| if path[0].as_inner() != CRATE_STR { |
| }; | ||
|
|
||
| // Phase 1: navigate to target and collect items. Immutable borrow, dropped at end of block | ||
| let collected: Vec<_> = { |
| /// | ||
| /// * Returns a specific error (e.g., [`Error::PrivateItem`], [`Error::RedefinedItem`]) if one occurred. | ||
| /// * Returns a fallback [`Error::UnresolvedItem`] if the item could not be found in any of the checked namespaces. | ||
| fn resolve_processing_use_items_error(results: &[Result<(), Error>]) -> Result<(), Error> { |
| /* | ||
| if !scope.module_path.is_empty() { | ||
| return Err(Error::MainOutOfEntryFile).with_span(from); | ||
| } | ||
| */ | ||
|
|
| /// THE DEFAULT HELPER | ||
| pub(crate) fn flatten_dependency_test(root_path: &str, lib_alias: &str) { | ||
| let root_path = PathBuf::from(root_path); | ||
| let lib_path = root_path.join(lib_alias); | ||
| let main_path = root_path.join(MAIN); | ||
| let flattened_path = root_path.join(FLATTENED); | ||
|
|
||
| let dependency_map = build_dependency_map(&main_path, [(&root_path, lib_alias, &lib_path)]); | ||
|
|
||
| let expected = format_program_file(&flattened_path); | ||
| let actual = flatten_template_file(&main_path, &dependency_map); | ||
|
|
||
| println!("Expected:\n{}", expected); | ||
| println!("----------"); | ||
| println!("Actual:\n{}", actual); | ||
|
|
||
| assert_eq!(expected, actual); | ||
| } |
| .assert_run_success(); | ||
| } | ||
|
|
||
| /// THE ADVANCED HELPER |
There was a problem hiding this comment.
| /// THE ADVANCED HELPER |
| .assert_run_success(); | ||
| } | ||
|
|
||
| /// THE DEFAULT HELPER |
There was a problem hiding this comment.
| /// THE DEFAULT HELPER |
| } | ||
|
|
||
| impl_eq_hash!(UseDecl; visibility, path, items); | ||
| impl_eq_hash!(UseDecl; file_id, visibility, path, items, span); |
There was a problem hiding this comment.
explain why those two new args are needed
ce3f943 to
693f2a2
Compare
b619e5c to
ec6cbb7
Compare
ec6cbb7 to
03e782d
Compare
Motivation
This PR is deliberately monolithic because the driver and AST resolution phases are tightly coupled. Updating
driver/resolve_order.rswithoutast.rs(or vice versa) breaks the test suite across both files, as well aslib.rs. The new driver outputs multi-file programs that the old AST cannot process, requiring them to be implemented and tested together.Changes
driver/resolve_order.rs: Stripped out alias and visibility resolution. The driver now strictly focuses on resolving multiple files and packing them intoItem::Moduleblocks.ast.rs: Implemented native scopes, alias resolution, and visibility checks. The analyze stage can now properly processItem::UseandItem::Moduleblocks.driver/resolve_order.rs,ast.rs, and globallib.rs).