Unnamed repository; edit this file 'description' to name the repository.
Auto merge of #17110 - Veykril:cargo-script-mvp, r=Veykril
Cargo script mvp Based on https://github.com/rust-lang/rust-analyzer/pull/15456, As the original PR stated, detached files are still horrendous to work with.
bors 2024-04-20
parent 8621e79 · parent 0ce7179 · commit c83d8cf
-rw-r--r--crates/load-cargo/src/lib.rs2
-rw-r--r--crates/project-model/src/cargo_workspace.rs24
-rw-r--r--crates/project-model/src/workspace.rs307
-rw-r--r--crates/rust-analyzer/src/cli/rustc_tests.rs5
-rw-r--r--crates/rust-analyzer/src/global_state.rs4
-rw-r--r--crates/rust-analyzer/src/handlers/notification.rs7
-rw-r--r--crates/rust-analyzer/src/handlers/request.rs6
-rw-r--r--crates/rust-analyzer/src/reload.rs43
-rw-r--r--crates/rust-analyzer/tests/slow-tests/main.rs99
-rw-r--r--crates/rust-analyzer/tests/slow-tests/support.rs22
10 files changed, 376 insertions, 143 deletions
diff --git a/crates/load-cargo/src/lib.rs b/crates/load-cargo/src/lib.rs
index ec37d88e60..31b0c8cdec 100644
--- a/crates/load-cargo/src/lib.rs
+++ b/crates/load-cargo/src/lib.rs
@@ -335,7 +335,7 @@ fn load_crate_graph(
) -> RootDatabase {
let (ProjectWorkspace::Cargo { toolchain, target_layout, .. }
| ProjectWorkspace::Json { toolchain, target_layout, .. }
- | ProjectWorkspace::DetachedFiles { toolchain, target_layout, .. }) = ws;
+ | ProjectWorkspace::DetachedFile { toolchain, target_layout, .. }) = ws;
let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok());
let mut db = RootDatabase::new(lru_cap);
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs
index fd898ffa5c..ff7cf144aa 100644
--- a/crates/project-model/src/cargo_workspace.rs
+++ b/crates/project-model/src/cargo_workspace.rs
@@ -305,6 +305,12 @@ impl CargoWorkspace {
.collect(),
);
}
+ // The manifest is a rust file, so this means its a script manifest
+ if cargo_toml.extension().is_some_and(|ext| ext == "rs") {
+ // Deliberately don't set up RUSTC_BOOTSTRAP or a nightly override here, the user should
+ // opt into it themselves.
+ other_options.push("-Zscript".to_owned());
+ }
meta.other_options(other_options);
// FIXME: Fetching metadata is a slow process, as it might require
@@ -373,11 +379,12 @@ impl CargoWorkspace {
let is_local = source.is_none();
let is_member = ws_members.contains(&id);
+ let manifest = AbsPathBuf::assert(manifest_path);
let pkg = packages.alloc(PackageData {
id: id.repr.clone(),
name,
version,
- manifest: AbsPathBuf::assert(manifest_path).try_into().unwrap(),
+ manifest: manifest.clone().try_into().unwrap(),
targets: Vec::new(),
is_local,
is_member,
@@ -400,11 +407,22 @@ impl CargoWorkspace {
for meta_tgt in meta_targets {
let cargo_metadata::Target { name, kind, required_features, src_path, .. } =
meta_tgt;
+ let kind = TargetKind::new(&kind);
let tgt = targets.alloc(TargetData {
package: pkg,
name,
- root: AbsPathBuf::assert(src_path),
- kind: TargetKind::new(&kind),
+ root: if kind == TargetKind::Bin
+ && manifest.extension().is_some_and(|ext| ext == "rs")
+ {
+ // cargo strips the script part of a cargo script away and places the
+ // modified manifest file into a special target dir which is then used as
+ // the source path. We don't want that, we want the original here so map it
+ // back
+ manifest.clone()
+ } else {
+ AbsPathBuf::assert(src_path)
+ },
+ kind,
required_features,
});
pkg_data.targets.push(tgt);
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index a5e74763d7..98c5a02dcd 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -99,9 +99,9 @@ pub enum ProjectWorkspace {
// //
/// Project with a set of disjoint files, not belonging to any particular workspace.
/// Backed by basic sysroot crates for basic completion and highlighting.
- DetachedFiles {
- /// The set of detached files.
- files: Vec<AbsPathBuf>,
+ DetachedFile {
+ /// The file in question.
+ file: AbsPathBuf,
/// The sysroot loaded for this workspace.
sysroot: Result<Sysroot, Option<String>>,
/// Holds cfg flags for the current target. We get those by running
@@ -115,6 +115,8 @@ pub enum ProjectWorkspace {
target_layout: TargetLayoutLoadResult,
/// A set of cfg overrides for the files.
cfg_overrides: CfgOverrides,
+ /// Is this file a cargo script file?
+ cargo_script: Option<CargoWorkspace>,
},
}
@@ -167,17 +169,20 @@ impl fmt::Debug for ProjectWorkspace {
.field("n_cfg_overrides", &cfg_overrides.len());
debug_struct.finish()
}
- ProjectWorkspace::DetachedFiles {
- files,
+ ProjectWorkspace::DetachedFile {
+ file,
sysroot,
rustc_cfg,
toolchain,
target_layout,
cfg_overrides,
+ cargo_script,
} => f
.debug_struct("DetachedFiles")
- .field("n_files", &files.len())
+ .field("file", &file)
+ .field("cargo_script", &cargo_script.is_some())
.field("sysroot", &sysroot.is_ok())
+ .field("cargo_script", &cargo_script.is_some())
.field("n_rustc_cfg", &rustc_cfg.len())
.field("toolchain", &toolchain)
.field("data_layout", &target_layout)
@@ -431,52 +436,79 @@ impl ProjectWorkspace {
pub fn load_detached_files(
detached_files: Vec<AbsPathBuf>,
config: &CargoConfig,
- ) -> anyhow::Result<ProjectWorkspace> {
- let dir = detached_files
- .first()
- .and_then(|it| it.parent())
- .ok_or_else(|| format_err!("No detached files to load"))?;
- let sysroot = match &config.sysroot {
- Some(RustLibSource::Path(path)) => {
- Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata)
- .map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}")))
- }
- Some(RustLibSource::Discover) => Sysroot::discover(
- dir,
- &config.extra_env,
- config.sysroot_query_metadata,
- )
- .map_err(|e| {
- Some(format!("Failed to find sysroot for {dir}. Is rust-src installed? {e}"))
- }),
- None => Err(None),
- };
+ ) -> Vec<anyhow::Result<ProjectWorkspace>> {
+ detached_files
+ .into_iter()
+ .map(|detached_file| {
+ let dir = detached_file
+ .parent()
+ .ok_or_else(|| format_err!("detached file has no parent"))?;
+ let sysroot = match &config.sysroot {
+ Some(RustLibSource::Path(path)) => {
+ Sysroot::with_sysroot_dir(path.clone(), config.sysroot_query_metadata)
+ .map_err(|e| Some(format!("Failed to find sysroot at {path}:{e}")))
+ }
+ Some(RustLibSource::Discover) => {
+ Sysroot::discover(dir, &config.extra_env, config.sysroot_query_metadata)
+ .map_err(|e| {
+ Some(format!(
+ "Failed to find sysroot for {dir}. Is rust-src installed? {e}"
+ ))
+ })
+ }
+ None => Err(None),
+ };
- let sysroot_ref = sysroot.as_ref().ok();
- let toolchain =
- match get_toolchain_version(dir, sysroot_ref, Tool::Rustc, &config.extra_env, "rustc ")
- {
- Ok(it) => it,
- Err(e) => {
- tracing::error!("{e}");
- None
- }
- };
+ let sysroot_ref = sysroot.as_ref().ok();
+ let toolchain = match get_toolchain_version(
+ dir,
+ sysroot_ref,
+ Tool::Rustc,
+ &config.extra_env,
+ "rustc ",
+ ) {
+ Ok(it) => it,
+ Err(e) => {
+ tracing::error!("{e}");
+ None
+ }
+ };
- let rustc_cfg = rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(sysroot_ref));
- let data_layout = target_data_layout::get(
- RustcDataLayoutConfig::Rustc(sysroot_ref),
- None,
- &config.extra_env,
- );
- Ok(ProjectWorkspace::DetachedFiles {
- files: detached_files,
- sysroot,
- rustc_cfg,
- toolchain,
- target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
- cfg_overrides: config.cfg_overrides.clone(),
- })
+ let rustc_cfg =
+ rustc_cfg::get(None, &config.extra_env, RustcCfgConfig::Rustc(sysroot_ref));
+ let data_layout = target_data_layout::get(
+ RustcDataLayoutConfig::Rustc(sysroot_ref),
+ None,
+ &config.extra_env,
+ );
+
+ let cargo_script = ManifestPath::try_from(detached_file.clone())
+ .ok()
+ .and_then(|file| {
+ CargoWorkspace::fetch_metadata(
+ &file,
+ file.parent(),
+ config,
+ sysroot_ref,
+ &|_| (),
+ )
+ .ok()
+ })
+ .map(CargoWorkspace::new);
+
+ Ok(ProjectWorkspace::DetachedFile {
+ file: detached_file,
+ sysroot,
+ rustc_cfg,
+ toolchain,
+ target_layout: data_layout
+ .map(Arc::from)
+ .map_err(|it| Arc::from(it.to_string())),
+ cfg_overrides: config.cfg_overrides.clone(),
+ cargo_script,
+ })
+ })
+ .collect()
}
/// Runs the build scripts for this [`ProjectWorkspace`].
@@ -486,7 +518,13 @@ impl ProjectWorkspace {
progress: &dyn Fn(String),
) -> anyhow::Result<WorkspaceBuildScripts> {
match self {
- ProjectWorkspace::Cargo { cargo, toolchain, sysroot, .. } => {
+ ProjectWorkspace::DetachedFile {
+ cargo_script: Some(cargo),
+ toolchain,
+ sysroot,
+ ..
+ }
+ | ProjectWorkspace::Cargo { cargo, toolchain, sysroot, .. } => {
WorkspaceBuildScripts::run_for_workspace(
config,
cargo,
@@ -498,9 +536,8 @@ impl ProjectWorkspace {
format!("Failed to run build scripts for {}", cargo.workspace_root())
})
}
- ProjectWorkspace::Json { .. } | ProjectWorkspace::DetachedFiles { .. } => {
- Ok(WorkspaceBuildScripts::default())
- }
+ ProjectWorkspace::DetachedFile { cargo_script: None, .. }
+ | ProjectWorkspace::Json { .. } => Ok(WorkspaceBuildScripts::default()),
}
}
@@ -556,11 +593,11 @@ impl ProjectWorkspace {
}
}
- pub fn workspace_definition_path(&self) -> Option<&AbsPath> {
+ pub fn workspace_definition_path(&self) -> &AbsPath {
match self {
- ProjectWorkspace::Cargo { cargo, .. } => Some(cargo.workspace_root()),
- ProjectWorkspace::Json { project, .. } => Some(project.path()),
- ProjectWorkspace::DetachedFiles { .. } => None,
+ ProjectWorkspace::Cargo { cargo, .. } => cargo.workspace_root(),
+ ProjectWorkspace::Json { project, .. } => project.path(),
+ ProjectWorkspace::DetachedFile { file, .. } => file,
}
}
@@ -568,10 +605,10 @@ impl ProjectWorkspace {
match self {
ProjectWorkspace::Cargo { sysroot: Ok(sysroot), .. }
| ProjectWorkspace::Json { sysroot: Ok(sysroot), .. }
- | ProjectWorkspace::DetachedFiles { sysroot: Ok(sysroot), .. } => {
+ | ProjectWorkspace::DetachedFile { sysroot: Ok(sysroot), .. } => {
sysroot.discover_proc_macro_srv()
}
- ProjectWorkspace::DetachedFiles { .. } => {
+ ProjectWorkspace::DetachedFile { .. } => {
Err(anyhow::format_err!("cannot find proc-macro server, no sysroot was found"))
}
ProjectWorkspace::Cargo { cargo, .. } => Err(anyhow::format_err!(
@@ -702,15 +739,50 @@ impl ProjectWorkspace {
}))
.collect()
}
- ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
- .iter()
- .map(|detached_file| PackageRoot {
+ ProjectWorkspace::DetachedFile { file, cargo_script, sysroot, .. } => {
+ iter::once(PackageRoot {
is_local: true,
- include: vec![detached_file.clone()],
+ include: vec![file.clone()],
exclude: Vec::new(),
})
+ .chain(cargo_script.iter().flat_map(|cargo| {
+ cargo.packages().map(|pkg| {
+ let is_local = cargo[pkg].is_local;
+ let pkg_root = cargo[pkg].manifest.parent().to_path_buf();
+
+ let mut include = vec![pkg_root.clone()];
+
+ // In case target's path is manually set in Cargo.toml to be
+ // outside the package root, add its parent as an extra include.
+ // An example of this situation would look like this:
+ //
+ // ```toml
+ // [lib]
+ // path = "../../src/lib.rs"
+ // ```
+ let extra_targets = cargo[pkg]
+ .targets
+ .iter()
+ .filter(|&&tgt| matches!(cargo[tgt].kind, TargetKind::Lib { .. }))
+ .filter_map(|&tgt| cargo[tgt].root.parent())
+ .map(|tgt| tgt.normalize().to_path_buf())
+ .filter(|path| !path.starts_with(&pkg_root));
+ include.extend(extra_targets);
+
+ let mut exclude = vec![pkg_root.join(".git")];
+ if is_local {
+ exclude.push(pkg_root.join("target"));
+ } else {
+ exclude.push(pkg_root.join("tests"));
+ exclude.push(pkg_root.join("examples"));
+ exclude.push(pkg_root.join("benches"));
+ }
+ PackageRoot { is_local, include, exclude }
+ })
+ }))
.chain(mk_sysroot(sysroot.as_ref()))
- .collect(),
+ .collect()
+ }
}
}
@@ -726,9 +798,10 @@ impl ProjectWorkspace {
let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages());
cargo.packages().len() + sysroot_package_len + rustc_package_len
}
- ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
+ ProjectWorkspace::DetachedFile { sysroot, cargo_script, .. } => {
let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages());
- sysroot_package_len + files.len()
+ sysroot_package_len
+ + cargo_script.as_ref().map_or(1, |cargo| cargo.packages().len())
}
}
}
@@ -781,21 +854,34 @@ impl ProjectWorkspace {
),
sysroot,
),
- ProjectWorkspace::DetachedFiles {
- files,
+ ProjectWorkspace::DetachedFile {
+ file,
sysroot,
rustc_cfg,
toolchain: _,
target_layout: _,
cfg_overrides,
+ cargo_script,
} => (
- detached_files_to_crate_graph(
- rustc_cfg.clone(),
- load,
- files,
- sysroot.as_ref().ok(),
- cfg_overrides,
- ),
+ if let Some(cargo) = cargo_script {
+ cargo_to_crate_graph(
+ &mut |path| load(path),
+ None,
+ cargo,
+ sysroot.as_ref().ok(),
+ rustc_cfg.clone(),
+ cfg_overrides,
+ &WorkspaceBuildScripts::default(),
+ )
+ } else {
+ detached_file_to_crate_graph(
+ rustc_cfg.clone(),
+ load,
+ file,
+ sysroot.as_ref().ok(),
+ cfg_overrides,
+ )
+ },
sysroot,
),
};
@@ -869,29 +955,32 @@ impl ProjectWorkspace {
&& cfg_overrides == o_cfg_overrides
}
(
- Self::DetachedFiles {
- files,
+ Self::DetachedFile {
+ file,
sysroot,
rustc_cfg,
+ cargo_script,
toolchain,
target_layout,
cfg_overrides,
},
- Self::DetachedFiles {
- files: o_files,
+ Self::DetachedFile {
+ file: o_file,
sysroot: o_sysroot,
rustc_cfg: o_rustc_cfg,
+ cargo_script: o_cargo_script,
toolchain: o_toolchain,
target_layout: o_target_layout,
cfg_overrides: o_cfg_overrides,
},
) => {
- files == o_files
+ file == o_file
&& sysroot == o_sysroot
&& rustc_cfg == o_rustc_cfg
&& toolchain == o_toolchain
&& target_layout == o_target_layout
&& cfg_overrides == o_cfg_overrides
+ && cargo_script == o_cargo_script
}
_ => false,
}
@@ -1202,10 +1291,10 @@ fn cargo_to_crate_graph(
res
}
-fn detached_files_to_crate_graph(
+fn detached_file_to_crate_graph(
rustc_cfg: Vec<CfgFlag>,
load: FileLoader<'_>,
- detached_files: &[AbsPathBuf],
+ detached_file: &AbsPathBuf,
sysroot: Option<&Sysroot>,
override_cfg: &CfgOverrides,
) -> (CrateGraph, ProcMacroPaths) {
@@ -1222,34 +1311,32 @@ fn detached_files_to_crate_graph(
override_cfg.apply(&mut cfg_options, "");
let cfg_options = Arc::new(cfg_options);
- for detached_file in detached_files {
- let file_id = match load(detached_file) {
- Some(file_id) => file_id,
- None => {
- tracing::error!("Failed to load detached file {:?}", detached_file);
- continue;
- }
- };
- let display_name = detached_file
- .file_stem()
- .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_owned()));
- let detached_file_crate = crate_graph.add_crate_root(
- file_id,
- Edition::CURRENT,
- display_name.clone(),
- None,
- cfg_options.clone(),
- None,
- Env::default(),
- false,
- CrateOrigin::Local {
- repo: None,
- name: display_name.map(|n| n.canonical_name().to_owned()),
- },
- );
+ let file_id = match load(detached_file) {
+ Some(file_id) => file_id,
+ None => {
+ tracing::error!("Failed to load detached file {:?}", detached_file);
+ return (crate_graph, FxHashMap::default());
+ }
+ };
+ let display_name = detached_file
+ .file_stem()
+ .map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_owned()));
+ let detached_file_crate = crate_graph.add_crate_root(
+ file_id,
+ Edition::CURRENT,
+ display_name.clone(),
+ None,
+ cfg_options.clone(),
+ None,
+ Env::default(),
+ false,
+ CrateOrigin::Local {
+ repo: None,
+ name: display_name.map(|n| n.canonical_name().to_owned()),
+ },
+ );
- public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate);
- }
+ public_deps.add_to_crate_graph(&mut crate_graph, detached_file_crate);
(crate_graph, FxHashMap::default())
}
diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs
index 548dd4e70e..2f9394d0ee 100644
--- a/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -75,13 +75,14 @@ impl Tester {
&cargo_config.extra_env,
);
- let workspace = ProjectWorkspace::DetachedFiles {
- files: vec![tmp_file],
+ let workspace = ProjectWorkspace::DetachedFile {
+ file: tmp_file,
sysroot,
rustc_cfg: vec![],
toolchain: None,
target_layout: data_layout.map(Arc::from).map_err(|it| Arc::from(it.to_string())),
cfg_overrides: Default::default(),
+ cargo_script: None,
};
let load_cargo_config = LoadCargoConfig {
load_out_dirs_from_check: false,
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index ec10bc7ccd..e9bca19af6 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -125,6 +125,7 @@ pub(crate) struct GlobalState {
/// to invalidate any salsa caches.
pub(crate) workspaces: Arc<Vec<ProjectWorkspace>>,
pub(crate) crate_graph_file_dependencies: FxHashSet<vfs::VfsPath>,
+ pub(crate) detached_files: FxHashSet<vfs::AbsPathBuf>,
// op queues
pub(crate) fetch_workspaces_queue:
@@ -233,6 +234,7 @@ impl GlobalState {
workspaces: Arc::from(Vec::new()),
crate_graph_file_dependencies: FxHashSet::default(),
+ detached_files: FxHashSet::default(),
fetch_workspaces_queue: OpQueue::default(),
fetch_build_data_queue: OpQueue::default(),
fetch_proc_macros_queue: OpQueue::default(),
@@ -519,7 +521,7 @@ impl GlobalStateSnapshot {
cargo.target_by_root(path).map(|it| (cargo, it))
}
ProjectWorkspace::Json { .. } => None,
- ProjectWorkspace::DetachedFiles { .. } => None,
+ ProjectWorkspace::DetachedFile { .. } => None,
})
}
diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs
index 68dc2cf2e2..4b8c3d06ce 100644
--- a/crates/rust-analyzer/src/handlers/notification.rs
+++ b/crates/rust-analyzer/src/handlers/notification.rs
@@ -154,6 +154,10 @@ pub(crate) fn handle_did_save_text_document(
state
.fetch_workspaces_queue
.request_op(format!("workspace vfs file change saved {abs_path}"), false);
+ } else if state.detached_files.contains(abs_path) {
+ state
+ .fetch_workspaces_queue
+ .request_op(format!("detached file saved {abs_path}"), false);
}
}
@@ -303,7 +307,8 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
}
None
}
- project_model::ProjectWorkspace::DetachedFiles { .. } => return None,
+ // FIXME
+ project_model::ProjectWorkspace::DetachedFile { .. } => return None,
};
Some((idx, package))
});
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index 9c86704ba3..cf97d7d9d2 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -101,7 +101,7 @@ pub(crate) fn handle_analyzer_status(
"Workspace root folders: {:?}",
snap.workspaces
.iter()
- .flat_map(|ws| ws.workspace_definition_path())
+ .map(|ws| ws.workspace_definition_path())
.collect::<Vec<&AbsPath>>()
);
}
@@ -1761,7 +1761,9 @@ pub(crate) fn handle_open_docs(
let ws_and_sysroot = snap.workspaces.iter().find_map(|ws| match ws {
ProjectWorkspace::Cargo { cargo, sysroot, .. } => Some((cargo, sysroot.as_ref().ok())),
ProjectWorkspace::Json { .. } => None,
- ProjectWorkspace::DetachedFiles { .. } => None,
+ ProjectWorkspace::DetachedFile { cargo_script, sysroot, .. } => {
+ cargo_script.as_ref().zip(Some(sysroot.as_ref().ok()))
+ }
});
let (cargo, sysroot) = match ws_and_sysroot {
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 00a61758f0..5d8a66cabc 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -153,7 +153,7 @@ impl GlobalState {
for ws in self.workspaces.iter() {
let (ProjectWorkspace::Cargo { sysroot, .. }
| ProjectWorkspace::Json { sysroot, .. }
- | ProjectWorkspace::DetachedFiles { sysroot, .. }) = ws;
+ | ProjectWorkspace::DetachedFile { sysroot, .. }) = ws;
match sysroot {
Err(None) => (),
Err(Some(e)) => {
@@ -255,7 +255,7 @@ impl GlobalState {
}
if !detached_files.is_empty() {
- workspaces.push(project_model::ProjectWorkspace::load_detached_files(
+ workspaces.extend(project_model::ProjectWorkspace::load_detached_files(
detached_files,
&cargo_config,
));
@@ -540,9 +540,6 @@ impl GlobalState {
}
fn recreate_crate_graph(&mut self, cause: String) {
- // crate graph construction relies on these paths, record them so when one of them gets
- // deleted or created we trigger a reconstruction of the crate graph
- let mut crate_graph_file_dependencies = mem::take(&mut self.crate_graph_file_dependencies);
self.report_progress(
"Building CrateGraph",
crate::lsp::utils::Progress::Begin,
@@ -551,13 +548,25 @@ impl GlobalState {
None,
);
+ // crate graph construction relies on these paths, record them so when one of them gets
+ // deleted or created we trigger a reconstruction of the crate graph
+ self.crate_graph_file_dependencies.clear();
+ self.detached_files = self
+ .workspaces
+ .iter()
+ .filter_map(|ws| match ws {
+ ProjectWorkspace::DetachedFile { file, .. } => Some(file.clone()),
+ _ => None,
+ })
+ .collect();
+
let (crate_graph, proc_macro_paths, layouts, toolchains) = {
// Create crate graph from all the workspaces
let vfs = &mut self.vfs.write().0;
let load = |path: &AbsPath| {
let vfs_path = vfs::VfsPath::from(path.to_path_buf());
- crate_graph_file_dependencies.insert(vfs_path.clone());
+ self.crate_graph_file_dependencies.insert(vfs_path.clone());
vfs.file_id(&vfs_path)
};
@@ -577,7 +586,6 @@ impl GlobalState {
change.set_target_data_layouts(layouts);
change.set_toolchains(toolchains);
self.analysis_host.apply_change(change);
- self.crate_graph_file_dependencies = crate_graph_file_dependencies;
self.report_progress(
"Building CrateGraph",
crate::lsp::utils::Progress::End,
@@ -674,7 +682,8 @@ impl GlobalState {
_ => None,
}
}
- ProjectWorkspace::DetachedFiles { .. } => None,
+ // FIXME
+ ProjectWorkspace::DetachedFile { .. } => None,
})
.map(|(id, root, sysroot_root)| {
let sender = sender.clone();
@@ -713,15 +722,9 @@ pub fn ws_to_crate_graph(
let (other, mut crate_proc_macros) = ws.to_crate_graph(&mut load, extra_env);
let num_layouts = layouts.len();
let num_toolchains = toolchains.len();
- let (toolchain, layout) = match ws {
- ProjectWorkspace::Cargo { toolchain, target_layout, .. }
- | ProjectWorkspace::Json { toolchain, target_layout, .. } => {
- (toolchain.clone(), target_layout.clone())
- }
- ProjectWorkspace::DetachedFiles { .. } => {
- (None, Err("detached files have no layout".into()))
- }
- };
+ let (ProjectWorkspace::Cargo { toolchain, target_layout, .. }
+ | ProjectWorkspace::Json { toolchain, target_layout, .. }
+ | ProjectWorkspace::DetachedFile { toolchain, target_layout, .. }) = ws;
let mapping = crate_graph.extend(
other,
@@ -730,7 +733,7 @@ pub fn ws_to_crate_graph(
// if the newly created crate graph's layout is equal to the crate of the merged graph, then
// we can merge the crates.
let id = cg_id.into_raw().into_u32() as usize;
- layouts[id] == layout && toolchains[id] == toolchain && cg_data == o_data
+ layouts[id] == *target_layout && toolchains[id] == *toolchain && cg_data == o_data
},
);
// Populate the side tables for the newly merged crates
@@ -742,13 +745,13 @@ pub fn ws_to_crate_graph(
if layouts.len() <= idx {
layouts.resize(idx + 1, e.clone());
}
- layouts[idx].clone_from(&layout);
+ layouts[idx].clone_from(target_layout);
}
if idx >= num_toolchains {
if toolchains.len() <= idx {
toolchains.resize(idx + 1, None);
}
- toolchains[idx].clone_from(&toolchain);
+ toolchains[idx].clone_from(toolchain);
}
});
proc_macro_paths.push(crate_proc_macros);
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs
index 786a80c4a6..b87f02947b 100644
--- a/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -118,6 +118,105 @@ fn f() {
}
#[test]
+fn completes_items_from_standard_library_in_cargo_script() {
+ // this test requires nightly so CI can't run it
+ if skip_slow_tests() || std::env::var("CI").is_ok() {
+ return;
+ }
+
+ let server = Project::with_fixture(
+ r#"
+//- /dependency/Cargo.toml
+[package]
+name = "dependency"
+version = "0.1.0"
+//- /dependency/src/lib.rs
+pub struct SpecialHashMap;
+//- /dependency2/Cargo.toml
+[package]
+name = "dependency2"
+version = "0.1.0"
+//- /dependency2/src/lib.rs
+pub struct SpecialHashMap2;
+//- /src/lib.rs
+#!/usr/bin/env -S cargo +nightly -Zscript
+---
+[dependencies]
+dependency = { path = "../dependency" }
+---
+use dependency::Spam;
+use dependency2::Spam;
+"#,
+ )
+ .with_config(serde_json::json!({
+ "cargo": { "sysroot": null },
+ }))
+ .server()
+ .wait_until_workspace_is_loaded();
+
+ let res = server.send_request::<Completion>(CompletionParams {
+ text_document_position: TextDocumentPositionParams::new(
+ server.doc_id("src/lib.rs"),
+ Position::new(5, 18),
+ ),
+ context: None,
+ partial_result_params: PartialResultParams::default(),
+ work_done_progress_params: WorkDoneProgressParams::default(),
+ });
+ assert!(res.to_string().contains("SpecialHashMap"), "{}", res.to_string());
+
+ let res = server.send_request::<Completion>(CompletionParams {
+ text_document_position: TextDocumentPositionParams::new(
+ server.doc_id("src/lib.rs"),
+ Position::new(6, 18),
+ ),
+ context: None,
+ partial_result_params: PartialResultParams::default(),
+ work_done_progress_params: WorkDoneProgressParams::default(),
+ });
+ assert!(!res.to_string().contains("SpecialHashMap"));
+
+ server.write_file_and_save(
+ "src/lib.rs",
+ r#"#!/usr/bin/env -S cargo +nightly -Zscript
+---
+[dependencies]
+dependency2 = { path = "../dependency2" }
+---
+use dependency::Spam;
+use dependency2::Spam;
+"#
+ .to_owned(),
+ );
+
+ let server = server.wait_until_workspace_is_loaded();
+
+ std::thread::sleep(std::time::Duration::from_secs(3));
+
+ let res = server.send_request::<Completion>(CompletionParams {
+ text_document_position: TextDocumentPositionParams::new(
+ server.doc_id("src/lib.rs"),
+ Position::new(5, 18),
+ ),
+ context: None,
+ partial_result_params: PartialResultParams::default(),
+ work_done_progress_params: WorkDoneProgressParams::default(),
+ });
+ assert!(!res.to_string().contains("SpecialHashMap"));
+
+ let res = server.send_request::<Completion>(CompletionParams {
+ text_document_position: TextDocumentPositionParams::new(
+ server.doc_id("src/lib.rs"),
+ Position::new(6, 18),
+ ),
+ context: None,
+ partial_result_params: PartialResultParams::default(),
+ work_done_progress_params: WorkDoneProgressParams::default(),
+ });
+ assert!(res.to_string().contains("SpecialHashMap"));
+}
+
+#[test]
fn test_runnables_project() {
if skip_slow_tests() {
return;
diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs
index c6778bf656..f04962a7a2 100644
--- a/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -125,7 +125,7 @@ impl Project<'_> {
}
let mut config = Config::new(
- tmp_dir_path,
+ tmp_dir_path.clone(),
lsp_types::ClientCapabilities {
workspace: Some(lsp_types::WorkspaceClientCapabilities {
did_change_watched_files: Some(
@@ -185,10 +185,14 @@ impl Project<'_> {
roots,
None,
);
- config.update(self.config).expect("invalid config");
+ // TODO: don't hardcode src/lib.rs as detached file
+ let mut c = self.config;
+ let p = tmp_dir_path.join("src/lib.rs").to_string();
+ c["detachedFiles"] = serde_json::json!([p]);
+ config.update(c).expect("invalid config");
config.rediscover_workspaces();
- Server::new(tmp_dir, config)
+ Server::new(tmp_dir.keep(), config)
}
}
@@ -283,6 +287,7 @@ impl Server {
}
}
+ #[track_caller]
pub(crate) fn send_request<R>(&self, params: R::Params) -> Value
where
R: lsp_types::request::Request,
@@ -294,6 +299,7 @@ impl Server {
let r = Request::new(id.into(), R::METHOD.to_owned(), params);
self.send_request_(r)
}
+ #[track_caller]
fn send_request_(&self, r: Request) -> Value {
let id = r.id.clone();
self.client.sender.send(r.clone().into()).unwrap();
@@ -374,6 +380,16 @@ impl Server {
pub(crate) fn path(&self) -> &Utf8Path {
self.dir.path()
}
+
+ pub(crate) fn write_file_and_save(&self, path: &str, text: String) {
+ fs::write(self.dir.path().join(path), &text).unwrap();
+ self.notification::<lsp_types::notification::DidSaveTextDocument>(
+ lsp_types::DidSaveTextDocumentParams {
+ text_document: self.doc_id(path),
+ text: Some(text),
+ },
+ )
+ }
}
impl Drop for Server {