@@ -7,7 +7,7 @@ use crate::error::{Error, ErrorCollector, RichError, Span};
77use crate :: parse:: { self , AliasedIdentifier , ParseFromStrWithErrors , Visibility } ;
88use crate :: str:: { AliasName , FunctionName , Identifier } ;
99use crate :: types:: AliasedType ;
10- use crate :: { get_full_path , impl_eq_hash, LibTable , SourceFile , SourceName } ;
10+ use crate :: { impl_eq_hash, DependencyMap , SourceFile , SourceName } ;
1111
1212/// Represents a single, isolated file in the SimplicityHL project.
1313/// In this architecture, a file and a module are the exact same thing.
@@ -28,8 +28,8 @@ pub struct ProjectGraph {
2828 pub ( self ) modules : Vec < Module > ,
2929
3030 /// The configuration environment.
31- /// Used to resolve xternal library dependencies and invoke their associated functions.
32- pub libraries : Arc < LibTable > ,
31+ /// Used to resolve external library dependencies and invoke their associated functions.
32+ pub dependency_map : Arc < DependencyMap > ,
3333
3434 /// Fast lookup: `SourceName` -> Module ID.
3535 /// A reverse index mapping absolute file paths to their internal IDs.
@@ -399,7 +399,7 @@ impl ProjectGraph {
399399
400400 pub fn new (
401401 root_source : SourceFile ,
402- libraries : Arc < LibTable > ,
402+ dependency_map : Arc < DependencyMap > ,
403403 root_program : & parse:: Program ,
404404 handler : & mut ErrorCollector ,
405405 ) -> Option < Self > {
@@ -434,11 +434,10 @@ impl ProjectGraph {
434434 // PHASE 1: Resolve Imports
435435 for elem in current_program. items ( ) {
436436 if let parse:: Item :: Use ( use_decl) = elem {
437- match get_full_path ( & libraries, use_decl) {
437+ match resolve_single_import ( importer_source. clone ( ) , use_decl, & dependency_map)
438+ {
438439 Ok ( path) => valid_imports. push ( ( path, * use_decl. span ( ) ) ) ,
439- Err ( err) => {
440- resolution_errors. push ( err. with_source ( importer_source. clone ( ) ) )
441- }
440+ Err ( err) => resolution_errors. push ( err) ,
442441 }
443442 }
444443 }
@@ -482,7 +481,7 @@ impl ProjectGraph {
482481 } else {
483482 Some ( Self {
484483 modules,
485- libraries ,
484+ dependency_map ,
486485 lookup,
487486 paths : paths. into ( ) ,
488487 dependencies,
@@ -691,13 +690,27 @@ impl ProjectGraph {
691690 let mut errors: Vec < RichError > = Vec :: new ( ) ;
692691 match elem {
693692 parse:: Item :: Use ( use_decl) => {
693+ let full_path = match resolve_single_import (
694+ importer_source. clone ( ) ,
695+ use_decl,
696+ & self . dependency_map ,
697+ ) {
698+ Ok ( path) => path,
699+ Err ( err) => {
700+ handler. push ( err) ;
701+ continue ;
702+ }
703+ } ;
704+
705+ /*
694706 let full_path = match get_full_path(&self.libraries, use_decl) {
695707 Ok(path) => path,
696708 Err(err) => {
697709 handler.push(err.with_source(importer_source.clone()));
698710 continue;
699711 }
700712 };
713+ */
701714 let source_full_path = SourceName :: Real ( Arc :: from ( full_path) ) ;
702715 let ind = self . lookup [ & source_full_path] ;
703716
@@ -776,6 +789,33 @@ impl ProjectGraph {
776789 }
777790}
778791
792+ /// Resolves a single `use` declaration into a physical file path.
793+ fn resolve_single_import (
794+ importer_source : SourceFile ,
795+ use_decl : & parse:: UseDecl ,
796+ dependency_map : & DependencyMap ,
797+ ) -> Result < PathBuf , RichError > {
798+ // TODO: @LesterEvSe or someone else, reconsider this architectural approach.
799+ // Consider removing this `match` statement, or dropping `SourceName` from `paths` and `lookup`.
800+ let curr_path = match importer_source. name ( ) {
801+ SourceName :: Real ( path) => path,
802+ SourceName :: Virtual ( name) => {
803+ // Notice we use `return Err(...)` here instead of `continue`
804+ return Err ( RichError :: new (
805+ Error :: Resolution ( format ! (
806+ "Virtual source '{name}' cannot be used to resolve library imports"
807+ ) ) ,
808+ * use_decl. span ( ) ,
809+ ) ) ;
810+ }
811+ } ;
812+
813+ match dependency_map. resolve_path ( & curr_path, use_decl) {
814+ Ok ( path) => Ok ( path) ,
815+ Err ( err) => Err ( err. with_source ( importer_source. clone ( ) ) ) ,
816+ }
817+ }
818+
779819/// C3 Merge Algorithm
780820///
781821/// Merges a list of sequences (parent linearizations) into a single sequence.
@@ -989,9 +1029,30 @@ pub(crate) mod tests {
9891029 ( program. expect ( "Root parsing failed internally" ) , source)
9901030 }
9911031
992- /// Sets up a graph with "lib" mapped to "libs/lib".
993- /// Files format: vec![("main.simf", "content"), ("libs/lib/A.simf", "content")]
994- fn setup_graph ( files : Vec < ( & str , & str ) > ) -> ( ProjectGraph , HashMap < String , usize > , TempDir ) {
1032+ /// Bootstraps a mock file system and attempts to construct a `ProjectGraph`.
1033+ ///
1034+ /// This is the low-level, non-panicking test helper. It is designed specifically for
1035+ /// "negative tests" where you expect the graph construction to fail (e.g., due to syntax
1036+ /// errors in an imported dependency).
1037+ ///
1038+ /// The mock environment automatically maps the alias `"lib"` to the `"libs/lib"` directory.
1039+ ///
1040+ /// # Arguments
1041+ /// * `files` - A vector of tuples containing `(file_path, file_content)`.
1042+ /// **Note:** One of the files *must* be named exactly `"main.simf"`.
1043+ ///
1044+ /// # Returns
1045+ /// A tuple containing:
1046+ /// 1. `Option<ProjectGraph>` - `Some` if construction succeeded, `None` if compilation failed.
1047+ /// 2. `ErrorCollector` - Contains all diagnostics emitted during parsing and resolution.
1048+ /// 3. `TempDir` - The temporary directory. It must be kept alive until the test completes.
1049+ ///
1050+ /// # Panics
1051+ /// Panics if the `files` vector does not contain a `"main.simf"` entry, or if writing
1052+ /// the mock files to the OS filesystem fails.
1053+ fn setup_graph_raw (
1054+ files : Vec < ( & str , & str ) > ,
1055+ ) -> ( Option < ProjectGraph > , ErrorCollector , TempDir ) {
9951056 let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
9961057
9971058 // 1. Create Files
@@ -1005,20 +1066,55 @@ pub(crate) mod tests {
10051066 let root_p = root_path. expect ( "Tests must define 'main.simf'" ) ;
10061067
10071068 // 2. Setup Libraries (Hardcoded "lib" -> "libs/lib" for simplicity in tests)
1008- let mut lib_map = HashMap :: new ( ) ;
1009- lib_map. insert ( "lib" . to_string ( ) , temp_dir. path ( ) . join ( "libs/lib" ) ) ;
1069+ let mut dependency_map = DependencyMap :: new ( ) ;
1070+ dependency_map. test_insert_without_canonicalize (
1071+ temp_dir. path ( ) , // The root of mock project
1072+ "lib" . to_string ( ) ,
1073+ & temp_dir. path ( ) . join ( "libs/lib" ) ,
1074+ ) ;
10101075
10111076 // 3. Parse & Build
10121077 let ( root_program, source) = parse_root ( & root_p) ;
1013-
10141078 let mut handler = ErrorCollector :: new ( ) ;
10151079
1016- let graph = ProjectGraph :: new ( source, Arc :: from ( lib_map) , & root_program, & mut handler)
1017- . expect (
1018- "setup_graph expects a valid graph construction. Use manual setup for error tests." ,
1019- ) ;
1080+ let result = ProjectGraph :: new (
1081+ source,
1082+ Arc :: from ( dependency_map) ,
1083+ & root_program,
1084+ & mut handler,
1085+ ) ;
1086+
1087+ // Return the raw result and the handler so the test can inspect the errors
1088+ ( result, handler, temp_dir)
1089+ }
10201090
1021- // 4. Create Lookup (File Name -> ID) for easier asserting
1091+ /// Bootstraps a mock file system and constructs a valid `ProjectGraph`.
1092+ ///
1093+ /// This is the standard test helper for "happy path" scenarios. It wraps [`setup_graph_raw`]
1094+ /// and mathematically guarantees that the graph construction succeeds. It also generates a
1095+ /// convenient filename-to-ID lookup map to make asserting on specific files easier.
1096+ ///
1097+ /// # Arguments
1098+ /// * `files` - A vector of tuples containing `(file_path, file_content)`.
1099+ /// **Note:** One of the files *must* be named exactly `"main.simf"`.
1100+ ///
1101+ /// # Returns
1102+ /// A tuple containing:
1103+ /// 1. `ProjectGraph` - The fully constructed, valid dependency graph.
1104+ /// 2. `HashMap<String, usize>` - A mapping of simple filenames (e.g., `"math.simf"`) to their node IDs.
1105+ /// 3. `TempDir` - The temporary directory. It must be kept alive until the test completes.
1106+ ///
1107+ /// # Panics
1108+ /// Panics if the compiler encounters any errors during parsing or resolution,
1109+ /// or if `"main.simf"` is missing. For testing compiler errors, use [`setup_graph_raw`] instead.
1110+ fn setup_graph ( files : Vec < ( & str , & str ) > ) -> ( ProjectGraph , HashMap < String , usize > , TempDir ) {
1111+ let ( graph_result, _handler, temp_dir) = setup_graph_raw ( files) ;
1112+
1113+ let graph = graph_result. expect (
1114+ "setup_graph expects a valid graph construction. Use manual setup for error tests." ,
1115+ ) ;
1116+
1117+ // Create Lookup (File Name -> ID) for easier asserting
10221118 let mut file_ids = HashMap :: new ( ) ;
10231119 for ( source_name, id) in & graph. lookup {
10241120 let simple_name = match source_name {
@@ -1319,37 +1415,7 @@ pub(crate) mod tests {
13191415 assert ! ( graph. dependencies[ & ids[ "main" ] ] . is_empty( ) ) ;
13201416 }
13211417
1322- #[ test]
1323- fn test_missing_file_error ( ) {
1324- // MANUAL SETUP REQUIRED
1325- // We cannot use `setup_graph` here because we expect `ProjectGraph::new` to fail/return None.
1326-
1327- let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
1328- let root_path = create_simf_file ( temp_dir. path ( ) , "main.simf" , "use lib::ghost::Phantom;" ) ;
1329- // We purposefully DO NOT create ghost.simf
1330-
1331- let mut lib_map = HashMap :: new ( ) ;
1332- lib_map. insert ( "lib" . to_string ( ) , temp_dir. path ( ) . join ( "libs/lib" ) ) ;
1333-
1334- let ( root_program, root_source) = parse_root ( & root_path) ;
1335- let mut handler = ErrorCollector :: new ( ) ;
1336-
1337- let result =
1338- ProjectGraph :: new ( root_source, Arc :: from ( lib_map) , & root_program, & mut handler) ;
1339-
1340- assert ! ( result. is_none( ) , "Graph construction should fail" ) ;
1341- assert ! ( handler. has_errors( ) ) ;
1342-
1343- let error_msg = ErrorCollector :: to_string ( & handler) ;
1344- assert ! (
1345- error_msg. contains( "File not found" ) || error_msg. contains( "ghost.simf" ) ,
1346- "Error message should mention 'ghost.simf' or 'File not found'. Got: {}" ,
1347- error_msg
1348- ) ;
1349- }
1350-
13511418 // Tests for aliases
1352- // TODO: @LesterEvSe, @Sdoba16 add more tests for alias
13531419 #[ test]
13541420 fn test_renaming_with_use ( ) {
13551421 // Scenario: Renaming imports.
@@ -1574,27 +1640,29 @@ Try reordering your `use` statements to avoid cross-wiring."#
15741640 }
15751641
15761642 // --- Dependent File Error Display Tests ---
1577-
15781643 #[ test]
1579- #[ ignore = "TODO(Error_Formatting): The compiler currently strips the .simf extension from file paths during graph construction. This test expects the extension to be preserved." ]
1580- fn test_display_error_in_imported_dependency ( ) {
1581- let temp_dir = TempDir :: new ( ) . unwrap ( ) ;
1582- let root_path = create_simf_file ( temp_dir. path ( ) , "main.simf" , "use lib::math::add;" ) ;
1583-
1584- create_simf_file (
1585- temp_dir. path ( ) ,
1586- "libs/lib/math.simf" ,
1587- "pub fn add(a: u32 b: u32) {}" ,
1588- ) ;
1644+ fn test_missing_file_error ( ) {
1645+ let ( result, handler, _dir) =
1646+ setup_graph_raw ( vec ! [ ( "main.simf" , "use lib::ghost::Phantom;" ) ] ) ;
15891647
1590- let mut lib_map = HashMap :: new ( ) ;
1591- lib_map . insert ( "lib" . to_string ( ) , temp_dir . path ( ) . join ( "libs/lib" ) ) ;
1648+ assert ! ( result . is_none ( ) , "Graph construction should fail" ) ;
1649+ assert ! ( handler . has_errors ( ) ) ;
15921650
1593- let ( root_program, root_source) = parse_root ( & root_path) ;
1594- let mut handler = ErrorCollector :: new ( ) ;
1651+ let error_msg = handler. to_string ( ) ;
1652+ assert ! (
1653+ error_msg. contains( "File not found" ) || error_msg. contains( "ghost.simf" ) ,
1654+ "Error message should mention 'ghost.simf' or 'File not found'. Got: {}" ,
1655+ error_msg
1656+ ) ;
1657+ }
15951658
1596- let result =
1597- ProjectGraph :: new ( root_source, Arc :: from ( lib_map) , & root_program, & mut handler) ;
1659+ #[ test]
1660+ #[ ignore = "TODO(Error_Formatting): The compiler currently strips the .simf extension from file paths during graph construction. This test expects the extension to be preserved." ]
1661+ fn test_display_error_in_imported_dependency ( ) {
1662+ let ( result, handler, _dir) = setup_graph_raw ( vec ! [
1663+ ( "main.simf" , "use lib::math::add;" ) ,
1664+ ( "libs/lib/math.simf" , "pub fn add(a: u32 b: u32) {}" ) , // NOTE: The comma is missing on purpose.
1665+ ] ) ;
15981666
15991667 assert ! (
16001668 result. is_none( ) ,
0 commit comments