@@ -27,6 +27,7 @@ use ide_db::{
2727use itertools:: Itertools ;
2828use proc_macro_api:: { MacroDylib , ProcMacroServer } ;
2929use project_model:: { PackageRoot , ProjectWorkspace , WorkspaceBuildScripts } ;
30+ use rustc_hash:: FxHashSet ;
3031use stdx:: { format_to, thread:: ThreadIntent } ;
3132use syntax:: SmolStr ;
3233use triomphe:: Arc ;
@@ -46,7 +47,7 @@ use ::tt::token_id as tt;
4647pub ( crate ) enum ProjectWorkspaceProgress {
4748 Begin ,
4849 Report ( String ) ,
49- End ( Vec < anyhow:: Result < ProjectWorkspace > > ) ,
50+ End ( Vec < anyhow:: Result < ProjectWorkspace > > , bool ) ,
5051}
5152
5253#[ derive( Debug ) ]
@@ -85,7 +86,7 @@ impl GlobalState {
8586 ) ;
8687 }
8788 if self . config . linked_projects ( ) != old_config. linked_projects ( ) {
88- self . fetch_workspaces_queue . request_op ( "linked projects changed" . to_string ( ) , ( ) )
89+ self . fetch_workspaces_queue . request_op ( "linked projects changed" . to_string ( ) , false )
8990 } else if self . config . flycheck ( ) != old_config. flycheck ( ) {
9091 self . reload_flycheck ( ) ;
9192 }
@@ -182,7 +183,7 @@ impl GlobalState {
182183 status
183184 }
184185
185- pub ( crate ) fn fetch_workspaces ( & mut self , cause : Cause ) {
186+ pub ( crate ) fn fetch_workspaces ( & mut self , cause : Cause , force_crate_graph_reload : bool ) {
186187 tracing:: info!( %cause, "will fetch workspaces" ) ;
187188
188189 self . task_pool . handle . spawn_with_sender ( ThreadIntent :: Worker , {
@@ -250,7 +251,10 @@ impl GlobalState {
250251
251252 tracing:: info!( "did fetch workspaces {:?}" , workspaces) ;
252253 sender
253- . send ( Task :: FetchWorkspace ( ProjectWorkspaceProgress :: End ( workspaces) ) )
254+ . send ( Task :: FetchWorkspace ( ProjectWorkspaceProgress :: End (
255+ workspaces,
256+ force_crate_graph_reload,
257+ ) ) )
254258 . unwrap ( ) ;
255259 }
256260 } ) ;
@@ -336,15 +340,19 @@ impl GlobalState {
336340 let _p = profile:: span ( "GlobalState::switch_workspaces" ) ;
337341 tracing:: info!( %cause, "will switch workspaces" ) ;
338342
343+ let Some ( ( workspaces, force_reload_crate_graph) ) = self . fetch_workspaces_queue . last_op_result ( ) else { return ; } ;
344+
339345 if let Err ( _) = self . fetch_workspace_error ( ) {
340346 if !self . workspaces . is_empty ( ) {
347+ if * force_reload_crate_graph {
348+ self . recreate_crate_graph ( cause) ;
349+ }
341350 // It only makes sense to switch to a partially broken workspace
342351 // if we don't have any workspace at all yet.
343352 return ;
344353 }
345354 }
346355
347- let Some ( workspaces) = self . fetch_workspaces_queue . last_op_result ( ) else { return ; } ;
348356 let workspaces =
349357 workspaces. iter ( ) . filter_map ( |res| res. as_ref ( ) . ok ( ) . cloned ( ) ) . collect :: < Vec < _ > > ( ) ;
350358
@@ -373,6 +381,9 @@ impl GlobalState {
373381 self . workspaces = Arc :: new ( workspaces) ;
374382 } else {
375383 tracing:: info!( "build scripts do not match the version of the active workspace" ) ;
384+ if * force_reload_crate_graph {
385+ self . recreate_crate_graph ( cause) ;
386+ }
376387 // Current build scripts do not match the version of the active
377388 // workspace, so there's nothing for us to update.
378389 return ;
@@ -467,13 +478,24 @@ impl GlobalState {
467478 } ) ;
468479 self . source_root_config = project_folders. source_root_config ;
469480
481+ self . recreate_crate_graph ( cause) ;
482+
483+ tracing:: info!( "did switch workspaces" ) ;
484+ }
485+
486+ fn recreate_crate_graph ( & mut self , cause : String ) {
470487 // Create crate graph from all the workspaces
471- let ( crate_graph, proc_macro_paths) = {
488+ let ( crate_graph, proc_macro_paths, crate_graph_file_dependencies ) = {
472489 let vfs = & mut self . vfs . write ( ) . 0 ;
473490 let loader = & mut self . loader ;
491+ // crate graph construction relies on these paths, record them so when one of them gets
492+ // deleted or created we trigger a reconstruction of the crate graph
493+ let mut crate_graph_file_dependencies = FxHashSet :: default ( ) ;
494+
474495 let mut load = |path : & AbsPath | {
475496 let _p = profile:: span ( "switch_workspaces::load" ) ;
476497 let vfs_path = vfs:: VfsPath :: from ( path. to_path_buf ( ) ) ;
498+ crate_graph_file_dependencies. insert ( vfs_path. clone ( ) ) ;
477499 match vfs. file_id ( & vfs_path) {
478500 Some ( file_id) => Some ( file_id) ,
479501 None => {
@@ -494,26 +516,25 @@ impl GlobalState {
494516 crate_graph. extend ( other, & mut crate_proc_macros) ;
495517 proc_macros. push ( crate_proc_macros) ;
496518 }
497- ( crate_graph, proc_macros)
519+ ( crate_graph, proc_macros, crate_graph_file_dependencies )
498520 } ;
499- let mut change = Change :: new ( ) ;
500521
501522 if self . config . expand_proc_macros ( ) {
502523 self . fetch_proc_macros_queue . request_op ( cause, proc_macro_paths) ;
503524 }
525+ let mut change = Change :: new ( ) ;
504526 change. set_crate_graph ( crate_graph) ;
505527 self . analysis_host . apply_change ( change) ;
528+ self . crate_graph_file_dependencies = crate_graph_file_dependencies;
506529 self . process_changes ( ) ;
507530
508531 self . reload_flycheck ( ) ;
509-
510- tracing:: info!( "did switch workspaces" ) ;
511532 }
512533
513534 pub ( super ) fn fetch_workspace_error ( & self ) -> Result < ( ) , String > {
514535 let mut buf = String :: new ( ) ;
515536
516- let Some ( last_op_result) = self . fetch_workspaces_queue . last_op_result ( ) else { return Ok ( ( ) ) } ;
537+ let Some ( ( last_op_result, _ ) ) = self . fetch_workspaces_queue . last_op_result ( ) else { return Ok ( ( ) ) } ;
517538 if last_op_result. is_empty ( ) {
518539 stdx:: format_to!( buf, "rust-analyzer failed to discover workspace" ) ;
519540 } else {
0 commit comments