Skip to content

Commit b322805

Browse files
committed
internal: Record file dependencies in crate graph construction
1 parent abe2495 commit b322805

File tree

6 files changed

+68
-31
lines changed

6 files changed

+68
-31
lines changed

crates/ide/src/syntax_highlighting.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,9 @@ fn traverse(
243243
let mut attr_or_derive_item = None;
244244
let mut current_macro: Option<ast::Macro> = None;
245245
let mut macro_highlighter = MacroHighlighter::default();
246+
247+
// FIXME: these are not perfectly accurate, we determine them by the real file's syntax tree
248+
// an an attribute nested in a macro call will not emit `inside_attribute`
246249
let mut inside_attribute = false;
247250
let mut inside_macro_call = false;
248251

crates/rust-analyzer/src/global_state.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use nohash_hasher::IntMap;
1414
use parking_lot::{Mutex, RwLock};
1515
use proc_macro_api::ProcMacroServer;
1616
use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
17-
use rustc_hash::FxHashMap;
17+
use rustc_hash::{FxHashMap, FxHashSet};
1818
use triomphe::Arc;
1919
use vfs::AnchoredPathBuf;
2020

@@ -112,9 +112,11 @@ pub(crate) struct GlobalState {
112112
/// the user just adds comments or whitespace to Cargo.toml, we do not want
113113
/// to invalidate any salsa caches.
114114
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
115+
pub(crate) crate_graph_file_dependencies: FxHashSet<vfs::VfsPath>,
115116

116117
// op queues
117-
pub(crate) fetch_workspaces_queue: OpQueue<(), Option<Vec<anyhow::Result<ProjectWorkspace>>>>,
118+
pub(crate) fetch_workspaces_queue:
119+
OpQueue<bool, Option<(Vec<anyhow::Result<ProjectWorkspace>>, bool)>>,
118120
pub(crate) fetch_build_data_queue:
119121
OpQueue<(), (Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,
120122
pub(crate) fetch_proc_macros_queue: OpQueue<Vec<ProcMacroPaths>, bool>,
@@ -196,6 +198,7 @@ impl GlobalState {
196198
vfs_progress_n_done: 0,
197199

198200
workspaces: Arc::new(Vec::new()),
201+
crate_graph_file_dependencies: FxHashSet::default(),
199202
fetch_workspaces_queue: OpQueue::default(),
200203
fetch_build_data_queue: OpQueue::default(),
201204
fetch_proc_macros_queue: OpQueue::default(),
@@ -209,10 +212,9 @@ impl GlobalState {
209212

210213
pub(crate) fn process_changes(&mut self) -> bool {
211214
let _p = profile::span("GlobalState::process_changes");
212-
let mut workspace_structure_change = None;
213215

214216
let mut file_changes = FxHashMap::default();
215-
let (change, changed_files) = {
217+
let (change, changed_files, workspace_structure_change) = {
216218
let mut change = Change::new();
217219
let (vfs, line_endings_map) = &mut *self.vfs.write();
218220
let changed_files = vfs.take_changes();
@@ -267,16 +269,20 @@ impl GlobalState {
267269
.map(|(file_id, (change_kind, _))| vfs::ChangedFile { file_id, change_kind })
268270
.collect();
269271

272+
let mut workspace_structure_change = None;
270273
// A file was added or deleted
271274
let mut has_structure_changes = false;
272275
for file in &changed_files {
273-
if let Some(path) = vfs.file_path(file.file_id).as_path() {
276+
let vfs_path = &vfs.file_path(file.file_id);
277+
if let Some(path) = vfs_path.as_path() {
274278
let path = path.to_path_buf();
275279
if reload::should_refresh_for_change(&path, file.change_kind) {
276-
workspace_structure_change = Some(path);
280+
workspace_structure_change = Some((path.clone(), false));
277281
}
278282
if file.is_created_or_deleted() {
279283
has_structure_changes = true;
284+
workspace_structure_change =
285+
Some((path, self.crate_graph_file_dependencies.contains(vfs_path)));
280286
}
281287
}
282288

@@ -301,7 +307,7 @@ impl GlobalState {
301307
let roots = self.source_root_config.partition(vfs);
302308
change.set_roots(roots);
303309
}
304-
(change, changed_files)
310+
(change, changed_files, workspace_structure_change)
305311
};
306312

307313
self.analysis_host.apply_change(change);
@@ -311,9 +317,11 @@ impl GlobalState {
311317
// FIXME: ideally we should only trigger a workspace fetch for non-library changes
312318
// but something's going wrong with the source root business when we add a new local
313319
// crate see https://github.com/rust-lang/rust-analyzer/issues/13029
314-
if let Some(path) = workspace_structure_change {
315-
self.fetch_workspaces_queue
316-
.request_op(format!("workspace vfs file change: {}", path.display()), ());
320+
if let Some((path, force_crate_graph_reload)) = workspace_structure_change {
321+
self.fetch_workspaces_queue.request_op(
322+
format!("workspace vfs file change: {}", path.display()),
323+
force_crate_graph_reload,
324+
);
317325
}
318326
self.proc_macro_changed =
319327
changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| {

crates/rust-analyzer/src/handlers/notification.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ pub(crate) fn handle_did_save_text_document(
127127
if reload::should_refresh_for_change(abs_path, ChangeKind::Modify) {
128128
state
129129
.fetch_workspaces_queue
130-
.request_op(format!("DidSaveTextDocument {}", abs_path.display()), ());
130+
.request_op(format!("DidSaveTextDocument {}", abs_path.display()), false);
131131
}
132132
}
133133

@@ -205,7 +205,7 @@ pub(crate) fn handle_did_change_workspace_folders(
205205

206206
if !config.has_linked_projects() && config.detached_files().is_empty() {
207207
config.rediscover_workspaces();
208-
state.fetch_workspaces_queue.request_op("client workspaces changed".to_string(), ())
208+
state.fetch_workspaces_queue.request_op("client workspaces changed".to_string(), false)
209209
}
210210

211211
Ok(())

crates/rust-analyzer/src/handlers/request.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result<
5151
state.proc_macro_clients = Arc::from(Vec::new());
5252
state.proc_macro_changed = false;
5353

54-
state.fetch_workspaces_queue.request_op("reload workspace request".to_string(), ());
54+
state.fetch_workspaces_queue.request_op("reload workspace request".to_string(), false);
5555
Ok(())
5656
}
5757

crates/rust-analyzer/src/main_loop.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -115,9 +115,11 @@ impl GlobalState {
115115
self.register_did_save_capability();
116116
}
117117

118-
self.fetch_workspaces_queue.request_op("startup".to_string(), ());
119-
if let Some((cause, ())) = self.fetch_workspaces_queue.should_start_op() {
120-
self.fetch_workspaces(cause);
118+
self.fetch_workspaces_queue.request_op("startup".to_string(), false);
119+
if let Some((cause, force_crate_graph_reload)) =
120+
self.fetch_workspaces_queue.should_start_op()
121+
{
122+
self.fetch_workspaces(cause, force_crate_graph_reload);
121123
}
122124

123125
while let Some(event) = self.next_event(&inbox) {
@@ -367,8 +369,10 @@ impl GlobalState {
367369
}
368370

369371
if self.config.cargo_autoreload() {
370-
if let Some((cause, ())) = self.fetch_workspaces_queue.should_start_op() {
371-
self.fetch_workspaces(cause);
372+
if let Some((cause, force_crate_graph_reload)) =
373+
self.fetch_workspaces_queue.should_start_op()
374+
{
375+
self.fetch_workspaces(cause, force_crate_graph_reload);
372376
}
373377
}
374378

@@ -471,8 +475,9 @@ impl GlobalState {
471475
let (state, msg) = match progress {
472476
ProjectWorkspaceProgress::Begin => (Progress::Begin, None),
473477
ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)),
474-
ProjectWorkspaceProgress::End(workspaces) => {
475-
self.fetch_workspaces_queue.op_completed(Some(workspaces));
478+
ProjectWorkspaceProgress::End(workspaces, force_reload_crate_graph) => {
479+
self.fetch_workspaces_queue
480+
.op_completed(Some((workspaces, force_reload_crate_graph)));
476481
if let Err(e) = self.fetch_workspace_error() {
477482
tracing::error!("FetchWorkspaceError:\n{e}");
478483
}

crates/rust-analyzer/src/reload.rs

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ use ide_db::{
2727
use itertools::Itertools;
2828
use proc_macro_api::{MacroDylib, ProcMacroServer};
2929
use project_model::{PackageRoot, ProjectWorkspace, WorkspaceBuildScripts};
30+
use rustc_hash::FxHashSet;
3031
use stdx::{format_to, thread::ThreadIntent};
3132
use syntax::SmolStr;
3233
use triomphe::Arc;
@@ -46,7 +47,7 @@ use ::tt::token_id as tt;
4647
pub(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

Comments
 (0)