@@ -85,11 +85,15 @@ impl SourceMap {
8585 }
8686 }
8787
88- fn insert ( & mut self , path : CanonPath ) -> usize {
88+ fn insert ( & mut self , path : CanonPath ) {
8989 let id = self . paths . len ( ) ;
9090 self . ids . insert ( path. clone ( ) , id) ;
9191 self . paths . push ( path) ;
92- id
92+ debug_assert_eq ! ( self . ids. len( ) , self . paths. len( ) ) ;
93+ }
94+
95+ fn len ( & self ) -> usize {
96+ self . paths . len ( )
9397 }
9498
9599 pub fn get_id ( & self , path : & CanonPath ) -> Option < usize > {
@@ -137,10 +141,10 @@ pub(crate) struct DependencyGraph {
137141 /// Fast bidirectional lookup between `CanonPath` and Module IDs.
138142 source_map : SourceMap ,
139143
140- /// Memoized results of [`crate::resolution::DependencyMap::resolve_path `] to avoid
141- /// resolving the same [`parse::UseDecl `] twice — once during the driver phase
142- /// and once during the building phase after linearization .
143- use_cache : HashMap < parse :: UseDecl , ResolvedUse > ,
144+ /// Memoizes [`crate::resolution::DependencyMap::resolve_path_internal `] results,
145+ /// keyed by the source [`Span `] of each `use` declaration, so the resolver runs
146+ /// once per occurrence and the linearization phase can look the result up directly .
147+ use_cache : HashMap < Span , ResolvedUse > ,
144148
145149 // TODO: Consider to optimising this with `Vec` instead of `HashMap`
146150 /// The Adjacency List: Defines the Directed acyclic Graph (DAG) of imports.
@@ -241,45 +245,8 @@ impl DependencyGraph {
241245 Ok ( ( !handler. has_errors ( ) ) . then_some ( graph) )
242246 }
243247
244- /// This helper cleanly encapsulates the process of loading source text, parsing it
245- /// into an `parse::Program`, and combining them so the compiler can easily work with the file.
246- /// If the file is missing or contains syntax errors, it logs the diagnostic to the
247- /// `ErrorCollector` and safely returns `None`.
248- fn parse_and_get_source_module (
249- path : & CanonPath ,
250- importer_source : & CanonSourceFile ,
251- span : Span ,
252- handler : & mut ErrorCollector ,
253- unstable_features : & UnstableFeatures ,
254- ) -> Option < SourceModule > {
255- let Ok ( content) = std:: fs:: read_to_string ( path. as_path ( ) ) else {
256- let err = RichError :: new (
257- Error :: FileNotFound {
258- filename : PathBuf :: from ( path. as_path ( ) ) ,
259- } ,
260- span,
261- )
262- . with_source ( importer_source. clone ( ) ) ;
263-
264- handler. push ( err) ;
265- return None ;
266- } ;
267-
268- let mut error_handler = ErrorCollector :: new ( ) ;
269- let source = CanonSourceFile :: new ( path. clone ( ) , Arc :: from ( content) ) ;
270-
271- let ast = parse:: Program :: parse_from_str_with_errors (
272- source. clone ( ) ,
273- unstable_features,
274- & mut error_handler,
275- ) ;
276-
277- if error_handler. has_errors ( ) {
278- handler. extend_with_handler ( source, & error_handler) ;
279- None
280- } else {
281- ast. map ( |program| SourceModule { source, program } )
282- }
248+ pub fn source_map ( & self ) -> & SourceMap {
249+ & self . source_map
283250 }
284251
285252 /// PHASE 1 OF GRAPH CONSTRUCTION: Resolves all `use` declarations within a single
@@ -292,7 +259,7 @@ impl DependencyGraph {
292259 current_program : & parse:: Program ,
293260 current_module : & CurrentModule ,
294261 dependency_map : & DependencyMap ,
295- use_cache : & mut HashMap < parse :: UseDecl , ResolvedUse > ,
262+ use_cache : & mut HashMap < Span , ResolvedUse > ,
296263 handler : & mut ErrorCollector ,
297264 ) -> Vec < ( CanonPath , Span ) > {
298265 let mut ctx = ImportContext {
@@ -330,7 +297,10 @@ impl DependencyGraph {
330297 continue ;
331298 }
332299
300+ let new_id = self . source_map . len ( ) ;
301+
333302 let Some ( module) = Self :: parse_and_get_source_module (
303+ new_id,
334304 & path,
335305 & current. source ,
336306 import_span,
@@ -342,7 +312,7 @@ impl DependencyGraph {
342312 continue ;
343313 } ;
344314
345- let new_id = self . source_map . insert ( path. clone ( ) ) ;
315+ self . source_map . insert ( path. clone ( ) ) ;
346316 self . modules . push ( module) ;
347317
348318 self . dependencies
@@ -353,8 +323,46 @@ impl DependencyGraph {
353323 }
354324 }
355325
356- pub fn source_map ( & self ) -> & SourceMap {
357- & self . source_map
326+ /// This helper cleanly encapsulates the process of loading source text, parsing it
327+ /// into an `parse::Program`, and combining them so the compiler can easily work with the file.
328+ /// If the file is missing or contains syntax errors, it logs the diagnostic to the
329+ /// `ErrorCollector` and safely returns `None`.
330+ fn parse_and_get_source_module (
331+ new_id : usize ,
332+ path : & CanonPath ,
333+ importer_source : & CanonSourceFile ,
334+ span : Span ,
335+ handler : & mut ErrorCollector ,
336+ unstable_features : & UnstableFeatures ,
337+ ) -> Option < SourceModule > {
338+ let Ok ( content) = std:: fs:: read_to_string ( path. as_path ( ) ) else {
339+ let err = RichError :: new (
340+ Error :: FileNotFound {
341+ filename : PathBuf :: from ( path. as_path ( ) ) ,
342+ } ,
343+ span,
344+ )
345+ . with_source ( importer_source. clone ( ) ) ;
346+
347+ handler. push ( err) ;
348+ return None ;
349+ } ;
350+
351+ let mut error_handler = ErrorCollector :: new ( ) ;
352+ let source = CanonSourceFile :: new ( path. clone ( ) , Arc :: from ( content) ) ;
353+ let ast = parse:: Program :: parse_from_str_with_errors (
354+ new_id,
355+ source. clone ( ) ,
356+ unstable_features,
357+ & mut error_handler,
358+ ) ;
359+
360+ if error_handler. has_errors ( ) {
361+ handler. extend_with_handler ( source, & error_handler) ;
362+ return None ;
363+ }
364+
365+ ast. map ( |program| SourceModule { source, program } )
358366 }
359367}
360368
@@ -363,11 +371,27 @@ impl DependencyGraph {
363371struct ImportContext < ' a > {
364372 current : CurrentModule ,
365373 dependency_map : & ' a DependencyMap ,
366- use_cache : & ' a mut HashMap < parse :: UseDecl , ResolvedUse > ,
374+ use_cache : & ' a mut HashMap < Span , ResolvedUse > ,
367375 handler : & ' a mut ErrorCollector ,
368376}
369377
370378impl < ' a > ImportContext < ' a > {
379+ /// Recursively walks an item, collecting resolved imports.
380+ /// Recurses into inline `mod` blocks.
381+ fn process_item ( & mut self , item : & parse:: Item , valid_imports : & mut Vec < ( CanonPath , Span ) > ) {
382+ match item {
383+ parse:: Item :: Use ( use_decl) => valid_imports. extend ( self . resolve_single ( use_decl) ) ,
384+ parse:: Item :: Module ( module) => {
385+ for item in module. items ( ) {
386+ self . process_item ( item, valid_imports) ;
387+ }
388+ }
389+
390+ // These items carry no import information at this stage and can be safely skipped.
391+ parse:: Item :: TypeAlias ( _) | parse:: Item :: Function ( _) | parse:: Item :: Ignored => { }
392+ }
393+ }
394+
371395 /// Resolves a single `use` declaration, caches the result for reuse during
372396 /// later graph construction phases, and returns the resolved path and span.
373397 /// Returns `None` and reports to `handler` if resolution fails.
@@ -384,18 +408,12 @@ impl<'a> ImportContext<'a> {
384408 }
385409 } ;
386410
387- // We assign a file ID to prevent collisions between identical `use` statements across different files.
388- // For instance, `use crate::A::foo;` in the local workspace and in an external dependency
389- // might resolve to completely different implementations of the `foo` function.
390- let mut use_decl = use_decl. clone ( ) ;
391411 let span = * use_decl. span ( ) ;
392- use_decl. set_file_id ( self . current . id ) ;
393-
394412 let result: ( CanonPath , Span ) = ( resolved. path . clone ( ) , span) ;
395413
396414 // Since we found an error, when we can reevalute the result, we do not want to break it again
397415 // So, add error to prevent similar cases in the future
398- if let Some ( old_value) = self . use_cache . insert ( use_decl , resolved) {
416+ if let Some ( old_value) = self . use_cache . insert ( span , resolved) {
399417 let msg = format ! (
400418 "Reevaluated an existing use_decl. Old value was: {:?}" ,
401419 old_value
@@ -406,26 +424,6 @@ impl<'a> ImportContext<'a> {
406424 }
407425 Some ( result)
408426 }
409-
410- /// Recursively walks an item, collecting resolved imports.
411- /// Recurses into inline `mod` blocks.
412- fn process_item ( & mut self , item : & parse:: Item , valid_imports : & mut Vec < ( CanonPath , Span ) > ) {
413- match item {
414- parse:: Item :: Use ( use_decl) => {
415- if let Some ( import) = self . resolve_single ( use_decl) {
416- valid_imports. push ( import) ;
417- }
418- }
419- parse:: Item :: Module ( module) => {
420- for item in module. items ( ) {
421- self . process_item ( item, valid_imports) ;
422- }
423- }
424-
425- // These items carry no import information at this stage and can be safely skipped.
426- parse:: Item :: TypeAlias ( _) | parse:: Item :: Function ( _) | parse:: Item :: Ignored => { }
427- }
428- }
429427}
430428
431429/// Shared mutable state threaded through dependency loading.
@@ -508,6 +506,7 @@ pub(crate) mod tests {
508506 let main_canon_source = CanonSourceFile :: new ( root_p, Arc :: from ( root_content) ) ;
509507
510508 let main_program_option = parse:: Program :: parse_from_str_with_errors (
509+ MAIN_MODULE ,
511510 main_canon_source. clone ( ) ,
512511 & UnstableFeatures :: all ( ) ,
513512 & mut handler,
0 commit comments