|
12 | 12 | //! correct. Instead, we try to provide a best-effort service. Even if the |
13 | 13 | //! project is currently loading and we don't have a full project model, we |
14 | 14 | //! still want to respond to various requests. |
15 | | -use std::{mem, sync::Arc}; |
| 15 | +use std::{collections::hash_map::Entry, mem, sync::Arc}; |
16 | 16 |
|
17 | 17 | use flycheck::{FlycheckConfig, FlycheckHandle}; |
18 | 18 | use hir::db::DefDatabase; |
19 | 19 | use ide::Change; |
20 | | -use ide_db::base_db::{ |
21 | | - CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, |
22 | | - ProcMacroLoadResult, SourceRoot, VfsPath, |
| 20 | +use ide_db::{ |
| 21 | + base_db::{ |
| 22 | + CrateGraph, Env, ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind, |
| 23 | + ProcMacroLoadResult, SourceRoot, VfsPath, |
| 24 | + }, |
| 25 | + FxHashMap, |
23 | 26 | }; |
| 27 | +use itertools::Itertools; |
24 | 28 | use proc_macro_api::{MacroDylib, ProcMacroServer}; |
25 | | -use project_model::{ProjectWorkspace, WorkspaceBuildScripts}; |
| 29 | +use project_model::{PackageRoot, ProjectWorkspace, WorkspaceBuildScripts}; |
26 | 30 | use syntax::SmolStr; |
27 | 31 | use vfs::{file_set::FileSetConfig, AbsPath, AbsPathBuf, ChangeKind}; |
28 | 32 |
|
@@ -494,7 +498,69 @@ impl ProjectFolders { |
494 | 498 | let mut fsc = FileSetConfig::builder(); |
495 | 499 | let mut local_filesets = vec![]; |
496 | 500 |
|
497 | | - for root in workspaces.iter().flat_map(|ws| ws.to_roots()) { |
| 501 | + // Dedup source roots |
| 502 | + // Depending on the project setup, we can have duplicated source roots, or for example in |
| 503 | + // the case of the rustc workspace, we can end up with two source roots that are almost the |
| 504 | + // same but not quite, like: |
| 505 | + // PackageRoot { is_local: false, include: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri")], exclude: [] } |
| 506 | + // PackageRoot { |
| 507 | + // is_local: true, |
| 508 | + // include: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri"), AbsPathBuf(".../rust/build/x86_64-pc-windows-msvc/stage0-tools/x86_64-pc-windows-msvc/release/build/cargo-miri-85801cd3d2d1dae4/out")], |
| 509 | + // exclude: [AbsPathBuf(".../rust/src/tools/miri/cargo-miri/.git"), AbsPathBuf(".../rust/src/tools/miri/cargo-miri/target")] |
| 510 | + // } |
| 511 | + // |
| 512 | + // The first one comes from the explicit rustc workspace which points to the rustc workspace itself |
| 513 | + // The second comes from the rustc workspace that we load as the actual project workspace |
| 514 | + // These `is_local` differing in this kind of way gives us problems, especially when trying to filter diagnostics as we don't report diagnostics for external libraries. |
| 515 | + // So we need to deduplicate these, usually it would be enough to deduplicate by `include`, but as the rustc example shows here that doesn't work, |
| 516 | + // so we need to also coalesce the includes if they overlap. |
| 517 | + |
| 518 | + let mut roots: Vec<_> = workspaces |
| 519 | + .iter() |
| 520 | + .flat_map(|ws| ws.to_roots()) |
| 521 | + .update(|root| root.include.sort()) |
| 522 | + .sorted_by(|a, b| a.include.cmp(&b.include)) |
| 523 | + .collect(); |
| 524 | + |
| 525 | + // map that tracks indices of overlapping roots |
| 526 | + let mut overlap_map = FxHashMap::<_, Vec<_>>::default(); |
| 527 | + let mut done = false; |
| 528 | + |
| 529 | + while !mem::replace(&mut done, true) { |
| 530 | + // maps include paths to indices of the corresponding root |
| 531 | + let mut include_to_idx = FxHashMap::default(); |
| 532 | + // Find and note down the indices of overlapping roots |
| 533 | + for (idx, root) in roots.iter().filter(|it| !it.include.is_empty()).enumerate() { |
| 534 | + for include in &root.include { |
| 535 | + match include_to_idx.entry(include) { |
| 536 | + Entry::Occupied(e) => { |
| 537 | + overlap_map.entry(*e.get()).or_default().push(idx); |
| 538 | + } |
| 539 | + Entry::Vacant(e) => { |
| 540 | + e.insert(idx); |
| 541 | + } |
| 542 | + } |
| 543 | + } |
| 544 | + } |
| 545 | + for (k, v) in overlap_map.drain() { |
| 546 | + done = false; |
| 547 | + for v in v { |
| 548 | + let r = mem::replace( |
| 549 | + &mut roots[v], |
| 550 | + PackageRoot { is_local: false, include: vec![], exclude: vec![] }, |
| 551 | + ); |
| 552 | + roots[k].is_local |= r.is_local; |
| 553 | + roots[k].include.extend(r.include); |
| 554 | + roots[k].exclude.extend(r.exclude); |
| 555 | + } |
| 556 | + roots[k].include.sort(); |
| 557 | + roots[k].exclude.sort(); |
| 558 | + roots[k].include.dedup(); |
| 559 | + roots[k].exclude.dedup(); |
| 560 | + } |
| 561 | + } |
| 562 | + |
| 563 | + for root in roots.into_iter().filter(|it| !it.include.is_empty()) { |
498 | 564 | let file_set_roots: Vec<VfsPath> = |
499 | 565 | root.include.iter().cloned().map(VfsPath::from).collect(); |
500 | 566 |
|
|
0 commit comments