Unnamed repository; edit this file 'description' to name the repository.
Allow rust files to be used linkedProjects
Lukas Wirth 2024-04-21
parent 55d9a53 · commit a2ed683
-rw-r--r--crates/base-db/src/lib.rs2
-rw-r--r--crates/hir/src/semantics/source_to_def.rs5
-rw-r--r--crates/ide-db/src/prime_caches.rs2
-rw-r--r--crates/project-model/src/build_scripts.rs53
-rw-r--r--crates/project-model/src/cargo_workspace.rs9
-rw-r--r--crates/project-model/src/lib.rs21
-rw-r--r--crates/project-model/src/manifest_path.rs12
-rw-r--r--crates/project-model/src/sysroot.rs6
-rw-r--r--crates/project-model/src/tests.rs24
-rw-r--r--crates/project-model/src/workspace.rs177
-rw-r--r--crates/rust-analyzer/src/cli/progress_report.rs2
-rw-r--r--crates/rust-analyzer/src/cli/rustc_tests.rs6
-rw-r--r--crates/rust-analyzer/src/config.rs12
-rw-r--r--crates/rust-analyzer/src/global_state.rs6
-rw-r--r--crates/rust-analyzer/src/handlers/request.rs9
-rw-r--r--crates/rust-analyzer/src/reload.rs11
-rw-r--r--crates/rust-analyzer/tests/crate_graph.rs10
-rw-r--r--crates/rust-analyzer/tests/slow-tests/main.rs1
-rw-r--r--crates/rust-analyzer/tests/slow-tests/support.rs6
-rw-r--r--docs/user/generated_config.adoc3
-rw-r--r--editors/code/package.json2
21 files changed, 201 insertions, 178 deletions
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index 2b64a07a5a..82aff6e3b8 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -51,6 +51,7 @@ pub trait FileLoader {
/// Text of the file.
fn file_text(&self, file_id: FileId) -> Arc<str>;
fn resolve_path(&self, path: AnchoredPath<'_>) -> Option<FileId>;
+ /// Crates whose root's source root is the same as the source root of `file_id`
fn relevant_crates(&self, file_id: FileId) -> Arc<[CrateId]>;
}
@@ -104,6 +105,7 @@ pub trait SourceDatabaseExt: SourceDatabase {
#[salsa::input]
fn source_root(&self, id: SourceRootId) -> Arc<SourceRoot>;
+ /// Crates whose root fool is in `id`.
fn source_root_crates(&self, id: SourceRootId) -> Arc<[CrateId]>;
}
diff --git a/crates/hir/src/semantics/source_to_def.rs b/crates/hir/src/semantics/source_to_def.rs
index 434e4b5a0c..007709a836 100644
--- a/crates/hir/src/semantics/source_to_def.rs
+++ b/crates/hir/src/semantics/source_to_def.rs
@@ -121,7 +121,7 @@ impl SourceToDefCtx<'_, '_> {
let _p = tracing::span!(tracing::Level::INFO, "SourceBinder::file_to_module_def").entered();
let mut mods = SmallVec::new();
for &crate_id in self.db.relevant_crates(file).iter() {
- // FIXME: inner items
+ // Note: `mod` declarations in block modules cannot be supported here
let crate_def_map = self.db.crate_def_map(crate_id);
mods.extend(
crate_def_map
@@ -129,6 +129,9 @@ impl SourceToDefCtx<'_, '_> {
.map(|local_id| crate_def_map.module_id(local_id)),
)
}
+ if mods.is_empty() {
+ // FIXME: detached file
+ }
mods
}
diff --git a/crates/ide-db/src/prime_caches.rs b/crates/ide-db/src/prime_caches.rs
index 024e8f6ae3..0db87c6bc4 100644
--- a/crates/ide-db/src/prime_caches.rs
+++ b/crates/ide-db/src/prime_caches.rs
@@ -1,6 +1,6 @@
//! rust-analyzer is lazy and doesn't compute anything unless asked. This
//! sometimes is counter productive when, for example, the first goto definition
-//! request takes longer to compute. This modules implemented prepopulation of
+//! request takes longer to compute. This module implements prepopulation of
//! various caches, it's not really advanced at the moment.
mod topologic_sort;
diff --git a/crates/project-model/src/build_scripts.rs b/crates/project-model/src/build_scripts.rs
index fbd423c9ea..3fdd59967b 100644
--- a/crates/project-model/src/build_scripts.rs
+++ b/crates/project-model/src/build_scripts.rs
@@ -24,7 +24,7 @@ use toolchain::Tool;
use crate::{
cfg::CfgFlag, utf8_stdout, CargoConfig, CargoFeatures, CargoWorkspace, InvocationLocation,
- InvocationStrategy, Package, Sysroot, TargetKind,
+ InvocationStrategy, ManifestPath, Package, Sysroot, TargetKind,
};
/// Output of the build script and proc-macro building steps for a workspace.
@@ -63,7 +63,7 @@ impl WorkspaceBuildScripts {
fn build_command(
config: &CargoConfig,
allowed_features: &FxHashSet<String>,
- workspace_root: &AbsPathBuf,
+ manifest_path: &ManifestPath,
sysroot: Option<&Sysroot>,
) -> io::Result<Command> {
let mut cmd = match config.run_build_script_command.as_deref() {
@@ -79,7 +79,7 @@ impl WorkspaceBuildScripts {
cmd.args(&config.extra_args);
cmd.arg("--manifest-path");
- cmd.arg(workspace_root.join("Cargo.toml"));
+ cmd.arg(manifest_path.as_ref());
if let Some(target_dir) = &config.target_dir {
cmd.arg("--target-dir").arg(target_dir);
@@ -116,6 +116,10 @@ impl WorkspaceBuildScripts {
}
}
+ if manifest_path.extension().map_or(false, |ext| ext == "rs") {
+ cmd.arg("-Zscript");
+ }
+
cmd
}
};
@@ -152,37 +156,12 @@ impl WorkspaceBuildScripts {
.as_ref();
let allowed_features = workspace.workspace_features();
-
- match Self::run_per_ws(
- Self::build_command(
- config,
- &allowed_features,
- &workspace.workspace_root().to_path_buf(),
- sysroot,
- )?,
- workspace,
- current_dir,
- progress,
- ) {
- Ok(WorkspaceBuildScripts { error: Some(error), .. })
- if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_75) =>
- {
- // building build scripts failed, attempt to build with --keep-going so
- // that we potentially get more build data
- let mut cmd = Self::build_command(
- config,
- &allowed_features,
- &workspace.workspace_root().to_path_buf(),
- sysroot,
- )?;
-
- cmd.args(["--keep-going"]);
- let mut res = Self::run_per_ws(cmd, workspace, current_dir, progress)?;
- res.error = Some(error);
- Ok(res)
- }
- res => res,
+ let mut cmd =
+ Self::build_command(config, &allowed_features, workspace.manifest_path(), sysroot)?;
+ if toolchain.as_ref().map_or(false, |it| *it >= RUST_1_75) {
+ cmd.args(["--keep-going"]);
}
+ Self::run_per_ws(cmd, workspace, current_dir, progress)
}
/// Runs the build scripts by invoking the configured command *once*.
@@ -204,7 +183,13 @@ impl WorkspaceBuildScripts {
))
}
};
- let cmd = Self::build_command(config, &Default::default(), workspace_root, None)?;
+ let cmd = Self::build_command(
+ config,
+ &Default::default(),
+ // This is not gonna be used anyways, so just construct a dummy here
+ &ManifestPath::try_from(workspace_root.clone()).unwrap(),
+ None,
+ )?;
// NB: Cargo.toml could have been modified between `cargo metadata` and
// `cargo check`. We shouldn't assume that package ids we see here are
// exactly those from `config`.
diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs
index ff7cf144aa..88ba5a0da9 100644
--- a/crates/project-model/src/cargo_workspace.rs
+++ b/crates/project-model/src/cargo_workspace.rs
@@ -32,6 +32,7 @@ pub struct CargoWorkspace {
targets: Arena<TargetData>,
workspace_root: AbsPathBuf,
target_directory: AbsPathBuf,
+ manifest_path: ManifestPath,
}
impl ops::Index<Package> for CargoWorkspace {
@@ -334,7 +335,7 @@ impl CargoWorkspace {
.with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()))
}
- pub fn new(mut meta: cargo_metadata::Metadata) -> CargoWorkspace {
+ pub fn new(mut meta: cargo_metadata::Metadata, manifest_path: ManifestPath) -> CargoWorkspace {
let mut pkg_by_id = FxHashMap::default();
let mut packages = Arena::default();
let mut targets = Arena::default();
@@ -448,7 +449,7 @@ impl CargoWorkspace {
let target_directory = AbsPathBuf::assert(meta.target_directory);
- CargoWorkspace { packages, targets, workspace_root, target_directory }
+ CargoWorkspace { packages, targets, workspace_root, target_directory, manifest_path }
}
pub fn packages(&self) -> impl ExactSizeIterator<Item = Package> + '_ {
@@ -466,6 +467,10 @@ impl CargoWorkspace {
&self.workspace_root
}
+ pub fn manifest_path(&self) -> &ManifestPath {
+ &self.manifest_path
+ }
+
pub fn target_directory(&self) -> &AbsPath {
&self.target_directory
}
diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs
index 7f3e35ca5d..5428f061b7 100644
--- a/crates/project-model/src/lib.rs
+++ b/crates/project-model/src/lib.rs
@@ -54,11 +54,13 @@ pub use crate::{
sysroot::Sysroot,
workspace::{FileLoader, PackageRoot, ProjectWorkspace},
};
+pub use cargo_metadata::Metadata;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum ProjectManifest {
ProjectJson(ManifestPath),
CargoToml(ManifestPath),
+ CargoScript(ManifestPath),
}
impl ProjectManifest {
@@ -71,7 +73,10 @@ impl ProjectManifest {
if path.file_name().unwrap_or_default() == "Cargo.toml" {
return Ok(ProjectManifest::CargoToml(path));
}
- bail!("project root must point to Cargo.toml or rust-project.json: {path}");
+ if path.extension().unwrap_or_default() == "rs" {
+ return Ok(ProjectManifest::CargoScript(path));
+ }
+ bail!("project root must point to a Cargo.toml, rust-project.json or <script>.rs file: {path}");
}
pub fn discover_single(path: &AbsPath) -> anyhow::Result<ProjectManifest> {
@@ -146,15 +151,19 @@ impl ProjectManifest {
res.sort();
res
}
+
+ pub fn manifest_path(&self) -> &ManifestPath {
+ match self {
+ ProjectManifest::ProjectJson(it)
+ | ProjectManifest::CargoToml(it)
+ | ProjectManifest::CargoScript(it) => it,
+ }
+ }
}
impl fmt::Display for ProjectManifest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- ProjectManifest::ProjectJson(it) | ProjectManifest::CargoToml(it) => {
- fmt::Display::fmt(&it, f)
- }
- }
+ fmt::Display::fmt(self.manifest_path(), f)
}
}
diff --git a/crates/project-model/src/manifest_path.rs b/crates/project-model/src/manifest_path.rs
index d86e81e7e1..6929a75895 100644
--- a/crates/project-model/src/manifest_path.rs
+++ b/crates/project-model/src/manifest_path.rs
@@ -1,5 +1,5 @@
//! See [`ManifestPath`].
-use std::{fmt, ops, path::Path};
+use std::{borrow::Borrow, fmt, ops};
use paths::{AbsPath, AbsPathBuf};
@@ -54,8 +54,14 @@ impl ops::Deref for ManifestPath {
}
}
-impl AsRef<Path> for ManifestPath {
- fn as_ref(&self) -> &Path {
+impl AsRef<AbsPath> for ManifestPath {
+ fn as_ref(&self) -> &AbsPath {
self.file.as_ref()
}
}
+
+impl Borrow<AbsPath> for ManifestPath {
+ fn borrow(&self) -> &AbsPath {
+ self.file.borrow()
+ }
+}
diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs
index 1142d6243d..00dd18b5b1 100644
--- a/crates/project-model/src/sysroot.rs
+++ b/crates/project-model/src/sysroot.rs
@@ -349,7 +349,7 @@ impl Sysroot {
.filter(|&package| RELEVANT_SYSROOT_CRATES.contains(&&*package.name))
.map(|package| package.id.clone())
.collect();
- let cargo_workspace = CargoWorkspace::new(res);
+ let cargo_workspace = CargoWorkspace::new(res, sysroot_cargo_toml);
Some(Sysroot {
root: sysroot_dir.clone(),
src_root: Some(Ok(sysroot_src_dir.clone())),
@@ -368,7 +368,7 @@ impl Sysroot {
.into_iter()
.map(|it| sysroot_src_dir.join(it))
.filter_map(|it| ManifestPath::try_from(it).ok())
- .find(|it| fs::metadata(it).is_ok());
+ .find(|it| fs::metadata(it.as_ref()).is_ok());
if let Some(root) = root {
stitched.crates.alloc(SysrootCrateData {
@@ -468,7 +468,7 @@ fn get_rustc_src(sysroot_path: &AbsPath) -> Option<ManifestPath> {
let rustc_src = sysroot_path.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml");
let rustc_src = ManifestPath::try_from(rustc_src).ok()?;
tracing::debug!("checking for rustc source code: {rustc_src}");
- if fs::metadata(&rustc_src).is_ok() {
+ if fs::metadata(rustc_src.as_ref()).is_ok() {
Some(rustc_src)
} else {
None
diff --git a/crates/project-model/src/tests.rs b/crates/project-model/src/tests.rs
index fd09dff503..41351d5dc0 100644
--- a/crates/project-model/src/tests.rs
+++ b/crates/project-model/src/tests.rs
@@ -1,6 +1,7 @@
use std::ops::Deref;
use base_db::{CrateGraph, FileId, ProcMacroPaths};
+use cargo_metadata::Metadata;
use cfg::{CfgAtom, CfgDiff};
use expect_test::{expect_file, ExpectFile};
use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
@@ -9,8 +10,8 @@ use serde::de::DeserializeOwned;
use triomphe::Arc;
use crate::{
- CargoWorkspace, CfgOverrides, ProjectJson, ProjectJsonData, ProjectWorkspace, Sysroot,
- WorkspaceBuildScripts,
+ CargoWorkspace, CfgOverrides, ManifestPath, ProjectJson, ProjectJsonData, ProjectWorkspace,
+ Sysroot, WorkspaceBuildScripts,
};
fn load_cargo(file: &str) -> (CrateGraph, ProcMacroPaths) {
@@ -21,8 +22,10 @@ fn load_cargo_with_overrides(
file: &str,
cfg_overrides: CfgOverrides,
) -> (CrateGraph, ProcMacroPaths) {
- let meta = get_test_json_file(file);
- let cargo_workspace = CargoWorkspace::new(meta);
+ let meta: Metadata = get_test_json_file(file);
+ let manifest_path =
+ ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap();
+ let cargo_workspace = CargoWorkspace::new(meta, manifest_path);
let project_workspace = ProjectWorkspace::Cargo {
cargo: cargo_workspace,
build_scripts: WorkspaceBuildScripts::default(),
@@ -41,8 +44,10 @@ fn load_cargo_with_fake_sysroot(
file_map: &mut FxHashMap<AbsPathBuf, FileId>,
file: &str,
) -> (CrateGraph, ProcMacroPaths) {
- let meta = get_test_json_file(file);
- let cargo_workspace = CargoWorkspace::new(meta);
+ let meta: Metadata = get_test_json_file(file);
+ let manifest_path =
+ ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap();
+ let cargo_workspace = CargoWorkspace::new(meta, manifest_path);
let project_workspace = ProjectWorkspace::Cargo {
cargo: cargo_workspace,
build_scripts: WorkspaceBuildScripts::default(),
@@ -268,9 +273,10 @@ fn smoke_test_real_sysroot_cargo() {
return;
}
let file_map = &mut FxHashMap::<AbsPathBuf, FileId>::default();
- let meta = get_test_json_file("hello-world-metadata.json");
-
- let cargo_workspace = CargoWorkspace::new(meta);
+ let meta: Metadata = get_test_json_file("hello-world-metadata.json");
+ let manifest_path =
+ ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap();
+ let cargo_workspace = CargoWorkspace::new(meta, manifest_path);
let sysroot = Ok(Sysroot::discover(
AbsPath::assert(Utf8Path::new(env!("CARGO_MANIFEST_DIR"))),
&Default::default(),
diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs
index 98c5a02dcd..9f6e75171b 100644
--- a/crates/project-model/src/workspace.rs
+++ b/crates/project-model/src/workspace.rs
@@ -4,7 +4,7 @@
use std::{collections::VecDeque, fmt, fs, iter, sync};
-use anyhow::{format_err, Context};
+use anyhow::Context;
use base_db::{
CrateDisplayName, CrateGraph, CrateId, CrateName, CrateOrigin, Dependency, Env, FileId,
LangCrateOrigin, ProcMacroPaths, TargetLayoutLoadResult,
@@ -14,7 +14,6 @@ use paths::{AbsPath, AbsPathBuf};
use rustc_hash::{FxHashMap, FxHashSet};
use semver::Version;
use span::Edition;
-use stdx::always;
use toolchain::Tool;
use triomphe::Arc;
@@ -101,7 +100,7 @@ pub enum ProjectWorkspace {
/// Backed by basic sysroot crates for basic completion and highlighting.
DetachedFile {
/// The file in question.
- file: AbsPathBuf,
+ file: ManifestPath,
/// The sysroot loaded for this workspace.
sysroot: Result<Sysroot, Option<String>>,
/// Holds cfg flags for the current target. We get those by running
@@ -116,7 +115,7 @@ pub enum ProjectWorkspace {
/// A set of cfg overrides for the files.
cfg_overrides: CfgOverrides,
/// Is this file a cargo script file?
- cargo_script: Option<CargoWorkspace>,
+ cargo_script: Option<(CargoWorkspace, WorkspaceBuildScripts)>,
},
}
@@ -230,7 +229,7 @@ impl ProjectWorkspace {
) -> anyhow::Result<ProjectWorkspace> {
let res = match manifest {
ProjectManifest::ProjectJson(project_json) => {
- let file = fs::read_to_string(project_json)
+ let file = fs::read_to_string(project_json.as_ref())
.with_context(|| format!("Failed to read json file {project_json}"))?;
let data = serde_json::from_str(&file)
.with_context(|| format!("Failed to deserialize json file {project_json}"))?;
@@ -243,6 +242,9 @@ impl ProjectWorkspace {
&config.cfg_overrides,
)
}
+ ProjectManifest::CargoScript(rust_file) => {
+ ProjectWorkspace::load_detached_file(rust_file, config)?
+ }
ProjectManifest::CargoToml(cargo_toml) => {
let sysroot = match (&config.sysroot, &config.sysroot_src) {
(Some(RustLibSource::Path(path)), None) => {
@@ -299,7 +301,7 @@ impl ProjectWorkspace {
progress,
) {
Ok(meta) => {
- let workspace = CargoWorkspace::new(meta);
+ let workspace = CargoWorkspace::new(meta, cargo_toml.clone());
let buildscripts = WorkspaceBuildScripts::rustc_crates(
&workspace,
cargo_toml.parent(),
@@ -355,7 +357,7 @@ impl ProjectWorkspace {
"Failed to read Cargo metadata from Cargo.toml file {cargo_toml}, {toolchain:?}",
)
})?;
- let cargo = CargoWorkspace::new(meta);
+ let cargo = CargoWorkspace::new(meta, cargo_toml.clone());
let cargo_config_extra_env =
cargo_config_env(cargo_toml, &config.extra_env, sysroot_ref);
@@ -433,82 +435,71 @@ impl ProjectWorkspace {
}
}
- pub fn load_detached_files(
- detached_files: Vec<AbsPathBuf>,
+ pub fn load_detached_file(
+ detached_file: &ManifestPath,
config: &CargoConfig,
- ) -> 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),
- };
+ ) -> anyhow::Result<ProjectWorkspace> {
+ let dir = detached_file.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,
- );
+ 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);
+ let cargo_script =
+ CargoWorkspace::fetch_metadata(detached_file, dir, config, sysroot_ref, &|_| ())
+ .ok()
+ .map(|ws| {
+ (
+ CargoWorkspace::new(ws, detached_file.clone()),
+ WorkspaceBuildScripts::default(),
+ )
+ });
- 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()
+ Ok(ProjectWorkspace::DetachedFile {
+ file: detached_file.to_owned(),
+ 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,
+ })
+ }
+
+ pub fn load_detached_files(
+ detached_files: Vec<ManifestPath>,
+ config: &CargoConfig,
+ ) -> Vec<anyhow::Result<ProjectWorkspace>> {
+ detached_files.into_iter().map(|file| Self::load_detached_file(&file, config)).collect()
}
/// Runs the build scripts for this [`ProjectWorkspace`].
@@ -519,7 +510,7 @@ impl ProjectWorkspace {
) -> anyhow::Result<WorkspaceBuildScripts> {
match self {
ProjectWorkspace::DetachedFile {
- cargo_script: Some(cargo),
+ cargo_script: Some((cargo, _)),
toolchain,
sysroot,
..
@@ -586,10 +577,11 @@ impl ProjectWorkspace {
pub fn set_build_scripts(&mut self, bs: WorkspaceBuildScripts) {
match self {
- ProjectWorkspace::Cargo { build_scripts, .. } => *build_scripts = bs,
- _ => {
- always!(bs == WorkspaceBuildScripts::default());
+ ProjectWorkspace::Cargo { build_scripts, .. }
+ | ProjectWorkspace::DetachedFile { cargo_script: Some((_, build_scripts)), .. } => {
+ *build_scripts = bs
}
+ _ => assert_eq!(bs, WorkspaceBuildScripts::default()),
}
}
@@ -742,15 +734,18 @@ impl ProjectWorkspace {
ProjectWorkspace::DetachedFile { file, cargo_script, sysroot, .. } => {
iter::once(PackageRoot {
is_local: true,
- include: vec![file.clone()],
+ include: vec![file.as_ref().to_owned()],
exclude: Vec::new(),
})
- .chain(cargo_script.iter().flat_map(|cargo| {
+ .chain(cargo_script.iter().flat_map(|(cargo, build_scripts)| {
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()];
+ let out_dir =
+ build_scripts.get_output(pkg).and_then(|it| it.out_dir.clone());
+ include.extend(out_dir);
// In case target's path is manually set in Cargo.toml to be
// outside the package root, add its parent as an extra include.
@@ -801,7 +796,7 @@ impl ProjectWorkspace {
ProjectWorkspace::DetachedFile { sysroot, cargo_script, .. } => {
let sysroot_package_len = sysroot.as_ref().map_or(0, |it| it.num_packages());
sysroot_package_len
- + cargo_script.as_ref().map_or(1, |cargo| cargo.packages().len())
+ + cargo_script.as_ref().map_or(1, |(cargo, _)| cargo.packages().len())
}
}
}
@@ -863,7 +858,7 @@ impl ProjectWorkspace {
cfg_overrides,
cargo_script,
} => (
- if let Some(cargo) = cargo_script {
+ if let Some((cargo, build_scripts)) = cargo_script {
cargo_to_crate_graph(
&mut |path| load(path),
None,
@@ -871,7 +866,7 @@ impl ProjectWorkspace {
sysroot.as_ref().ok(),
rustc_cfg.clone(),
cfg_overrides,
- &WorkspaceBuildScripts::default(),
+ build_scripts,
)
} else {
detached_file_to_crate_graph(
@@ -959,7 +954,7 @@ impl ProjectWorkspace {
file,
sysroot,
rustc_cfg,
- cargo_script,
+ cargo_script: Some((cargo_script, _)),
toolchain,
target_layout,
cfg_overrides,
@@ -968,7 +963,7 @@ impl ProjectWorkspace {
file: o_file,
sysroot: o_sysroot,
rustc_cfg: o_rustc_cfg,
- cargo_script: o_cargo_script,
+ cargo_script: Some((o_cargo_script, _)),
toolchain: o_toolchain,
target_layout: o_target_layout,
cfg_overrides: o_cfg_overrides,
@@ -1294,11 +1289,11 @@ fn cargo_to_crate_graph(
fn detached_file_to_crate_graph(
rustc_cfg: Vec<CfgFlag>,
load: FileLoader<'_>,
- detached_file: &AbsPathBuf,
+ detached_file: &ManifestPath,
sysroot: Option<&Sysroot>,
override_cfg: &CfgOverrides,
) -> (CrateGraph, ProcMacroPaths) {
- let _p = tracing::span!(tracing::Level::INFO, "detached_files_to_crate_graph").entered();
+ let _p = tracing::span!(tracing::Level::INFO, "detached_file_to_crate_graph").entered();
let mut crate_graph = CrateGraph::default();
let (public_deps, _libproc_macro) = match sysroot {
Some(sysroot) => sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load),
diff --git a/crates/rust-analyzer/src/cli/progress_report.rs b/crates/rust-analyzer/src/cli/progress_report.rs
index 6964977840..8b143daf2a 100644
--- a/crates/rust-analyzer/src/cli/progress_report.rs
+++ b/crates/rust-analyzer/src/cli/progress_report.rs
@@ -96,7 +96,7 @@ impl<'a> ProgressReport<'a> {
}
fn set_value(&mut self, value: f32) {
- self.curr = f32::max(0.0, f32::min(1.0, value));
+ self.curr = value.clamp(0.0, 1.0);
}
fn clear(&mut self) {
diff --git a/crates/rust-analyzer/src/cli/rustc_tests.rs b/crates/rust-analyzer/src/cli/rustc_tests.rs
index 2f9394d0ee..cd15d86aa6 100644
--- a/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -10,7 +10,9 @@ use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig};
use itertools::Either;
use profile::StopWatch;
use project_model::target_data_layout::RustcDataLayoutConfig;
-use project_model::{target_data_layout, CargoConfig, ProjectWorkspace, RustLibSource, Sysroot};
+use project_model::{
+ target_data_layout, CargoConfig, ManifestPath, ProjectWorkspace, RustLibSource, Sysroot,
+};
use load_cargo::{load_workspace, LoadCargoConfig, ProcMacroServerChoice};
use rustc_hash::FxHashMap;
@@ -76,7 +78,7 @@ impl Tester {
);
let workspace = ProjectWorkspace::DetachedFile {
- file: tmp_file,
+ file: ManifestPath::try_from(tmp_file).unwrap(),
sysroot,
rustc_cfg: vec![],
toolchain: None,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index e956791d9d..5842c1d2ec 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -356,7 +356,8 @@ config_data! {
/// of projects.
///
/// Elements must be paths pointing to `Cargo.toml`,
- /// `rust-project.json`, or JSON objects in `rust-project.json` format.
+ /// `rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON
+ /// objects in `rust-project.json` format.
linkedProjects: Vec<ManifestOrProjectJson> = vec![],
/// Number of syntax trees rust-analyzer keeps in memory. Defaults to 128.
@@ -1301,12 +1302,9 @@ impl Config {
self.files_excludeDirs().iter().map(|p| self.root_path.join(p)).collect();
self.discovered_projects
.iter()
- .filter(
- |(ProjectManifest::ProjectJson(path)
- | ProjectManifest::CargoToml(path))| {
- !exclude_dirs.iter().any(|p| path.starts_with(p))
- },
- )
+ .filter(|project| {
+ !exclude_dirs.iter().any(|p| project.manifest_path().starts_with(p))
+ })
.cloned()
.map(LinkedProject::from)
.collect()
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index e9bca19af6..eaf3f511c4 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -18,7 +18,9 @@ use parking_lot::{
RwLockWriteGuard,
};
use proc_macro_api::ProcMacroServer;
-use project_model::{CargoWorkspace, ProjectWorkspace, Target, WorkspaceBuildScripts};
+use project_model::{
+ CargoWorkspace, ManifestPath, ProjectWorkspace, Target, WorkspaceBuildScripts,
+};
use rustc_hash::{FxHashMap, FxHashSet};
use triomphe::Arc;
use vfs::{AnchoredPathBuf, ChangedFile, Vfs};
@@ -125,7 +127,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>,
+ pub(crate) detached_files: FxHashSet<ManifestPath>,
// op queues
pub(crate) fetch_workspaces_queue:
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index cf97d7d9d2..39cdef08f8 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -1759,11 +1759,12 @@ pub(crate) fn handle_open_docs(
let position = from_proto::file_position(&snap, params)?;
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::DetachedFile { cargo_script, sysroot, .. } => {
- cargo_script.as_ref().zip(Some(sysroot.as_ref().ok()))
+ ProjectWorkspace::Cargo { cargo, sysroot, .. }
+ | ProjectWorkspace::DetachedFile { cargo_script: Some((cargo, _)), sysroot, .. } => {
+ Some((cargo, sysroot.as_ref().ok()))
}
+ ProjectWorkspace::Json { .. } => None,
+ ProjectWorkspace::DetachedFile { .. } => None,
});
let (cargo, sysroot) = match ws_and_sysroot {
diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs
index 5d8a66cabc..4cc174731c 100644
--- a/crates/rust-analyzer/src/reload.rs
+++ b/crates/rust-analyzer/src/reload.rs
@@ -25,7 +25,7 @@ use ide_db::{
use itertools::Itertools;
use load_cargo::{load_proc_macro, ProjectFolders};
use proc_macro_api::ProcMacroServer;
-use project_model::{ProjectWorkspace, WorkspaceBuildScripts};
+use project_model::{ManifestPath, ProjectWorkspace, WorkspaceBuildScripts};
use stdx::{format_to, thread::ThreadIntent};
use triomphe::Arc;
use vfs::{AbsPath, AbsPathBuf, ChangeKind};
@@ -204,7 +204,14 @@ impl GlobalState {
self.task_pool.handle.spawn_with_sender(ThreadIntent::Worker, {
let linked_projects = self.config.linked_or_discovered_projects();
- let detached_files = self.config.detached_files().to_vec();
+ let detached_files: Vec<_> = self
+ .config
+ .detached_files()
+ .iter()
+ .cloned()
+ .map(ManifestPath::try_from)
+ .filter_map(Result::ok)
+ .collect();
let cargo_config = self.config.cargo();
move |sender| {
diff --git a/crates/rust-analyzer/tests/crate_graph.rs b/crates/rust-analyzer/tests/crate_graph.rs
index cf38032b94..e833ea5e11 100644
--- a/crates/rust-analyzer/tests/crate_graph.rs
+++ b/crates/rust-analyzer/tests/crate_graph.rs
@@ -1,14 +1,18 @@
use std::path::PathBuf;
-use project_model::{CargoWorkspace, ProjectWorkspace, Sysroot, WorkspaceBuildScripts};
+use project_model::{
+ CargoWorkspace, ManifestPath, Metadata, ProjectWorkspace, Sysroot, WorkspaceBuildScripts,
+};
use rust_analyzer::ws_to_crate_graph;
use rustc_hash::FxHashMap;
use serde::de::DeserializeOwned;
use vfs::{AbsPathBuf, FileId};
fn load_cargo_with_fake_sysroot(file: &str) -> ProjectWorkspace {
- let meta = get_test_json_file(file);
- let cargo_workspace = CargoWorkspace::new(meta);
+ let meta: Metadata = get_test_json_file(file);
+ let manifest_path =
+ ManifestPath::try_from(AbsPathBuf::try_from(meta.workspace_root.clone()).unwrap()).unwrap();
+ let cargo_workspace = CargoWorkspace::new(meta, manifest_path);
ProjectWorkspace::Cargo {
cargo: cargo_workspace,
build_scripts: WorkspaceBuildScripts::default(),
diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs
index b87f02947b..2ca86bc50a 100644
--- a/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -150,6 +150,7 @@ use dependency2::Spam;
)
.with_config(serde_json::json!({
"cargo": { "sysroot": null },
+ "detachedFiles": ["/src/lib.rs"],
}))
.server()
.wait_until_workspace_is_loaded();
diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs
index f04962a7a2..cf27cc7eef 100644
--- a/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -185,11 +185,7 @@ impl Project<'_> {
roots,
None,
);
- // 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.update(self.config).expect("invalid config");
config.rediscover_workspaces();
Server::new(tmp_dir.keep(), config)
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index a03ab0031d..6e4820c5de 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -778,7 +778,8 @@ Disable project auto-discovery in favor of explicitly specified set
of projects.
Elements must be paths pointing to `Cargo.toml`,
-`rust-project.json`, or JSON objects in `rust-project.json` format.
+`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON
+objects in `rust-project.json` format.
--
[[rust-analyzer.lru.capacity]]rust-analyzer.lru.capacity (default: `null`)::
+
diff --git a/editors/code/package.json b/editors/code/package.json
index 389e1b8742..352eb24229 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -1495,7 +1495,7 @@
"type": "boolean"
},
"rust-analyzer.linkedProjects": {
- "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set\nof projects.\n\nElements must be paths pointing to `Cargo.toml`,\n`rust-project.json`, or JSON objects in `rust-project.json` format.",
+ "markdownDescription": "Disable project auto-discovery in favor of explicitly specified set\nof projects.\n\nElements must be paths pointing to `Cargo.toml`,\n`rust-project.json`, `.rs` files (which will be treated as standalone files) or JSON\nobjects in `rust-project.json` format.",
"default": [],
"type": "array",
"items": {