diff --git a/crates/project-model/src/cargo_workspace.rs b/crates/project-model/src/cargo_workspace.rs index 38eeedec62..db9c20fdc5 100644 --- a/crates/project-model/src/cargo_workspace.rs +++ b/crates/project-model/src/cargo_workspace.rs @@ -33,6 +33,8 @@ pub struct CargoWorkspace { workspace_root: AbsPathBuf, target_directory: AbsPathBuf, manifest_path: ManifestPath, + // Whether this workspace was queried with `--no-deps`. + no_deps: bool, } impl ops::Index for CargoWorkspace { @@ -259,6 +261,18 @@ impl CargoWorkspace { sysroot: &Sysroot, locked: bool, progress: &dyn Fn(String), + ) -> anyhow::Result { + Self::fetch_metadata_(cargo_toml, current_dir, config, sysroot, locked, false, progress) + } + + fn fetch_metadata_( + cargo_toml: &ManifestPath, + current_dir: &AbsPath, + config: &CargoConfig, + sysroot: &Sysroot, + locked: bool, + no_deps: bool, + progress: &dyn Fn(String), ) -> anyhow::Result { let targets = find_list_of_build_targets(config, cargo_toml, sysroot); @@ -314,6 +328,9 @@ impl CargoWorkspace { if locked { other_options.push("--locked".to_owned()); } + if no_deps { + other_options.push("--no-deps".to_owned()); + } meta.other_options(other_options); // FIXME: Fetching metadata is a slow process, as it might require @@ -324,6 +341,22 @@ impl CargoWorkspace { (|| -> Result { let output = meta.cargo_command().output()?; if !output.status.success() { + if !no_deps { + // If we failed to fetch metadata with deps, try again without them. + // This makes r-a still work partially when offline. + if let Ok(metadata) = Self::fetch_metadata_( + cargo_toml, + current_dir, + config, + sysroot, + locked, + true, + progress, + ) { + return Ok(metadata); + } + } + return Err(cargo_metadata::Error::CargoMetadata { stderr: String::from_utf8(output.stderr)?, }); @@ -431,8 +464,8 @@ impl CargoWorkspace { pkg_data.targets.push(tgt); } } - let resolve = meta.resolve.expect("metadata executed with deps"); - for mut node in resolve.nodes { + let no_deps = meta.resolve.is_none(); + for mut node in meta.resolve.map_or_else(Vec::new, |it| it.nodes) { let &source = pkg_by_id.get(&node.id).unwrap(); node.deps.sort_by(|a, b| a.pkg.cmp(&b.pkg)); let dependencies = node @@ -451,7 +484,14 @@ impl CargoWorkspace { let target_directory = AbsPathBuf::assert(meta.target_directory); - CargoWorkspace { packages, targets, workspace_root, target_directory, manifest_path } + CargoWorkspace { + packages, + targets, + workspace_root, + target_directory, + manifest_path, + no_deps, + } } pub fn packages(&self) -> impl ExactSizeIterator + '_ { @@ -533,6 +573,10 @@ impl CargoWorkspace { fn is_unique(&self, name: &str) -> bool { self.packages.iter().filter(|(_, v)| v.name == name).count() == 1 } + + pub fn no_deps(&self) -> bool { + self.no_deps + } } fn find_list_of_build_targets( diff --git a/crates/project-model/src/sysroot.rs b/crates/project-model/src/sysroot.rs index 419fac3f41..e11f0ee3ae 100644 --- a/crates/project-model/src/sysroot.rs +++ b/crates/project-model/src/sysroot.rs @@ -372,18 +372,19 @@ impl Sysroot { .flatten() }; - let resolve = res.resolve.as_mut().expect("metadata executed with deps"); - resolve.nodes.retain_mut(|node| { - // Replace `rustc-std-workspace` crate with the actual one in the dependency list - node.deps.iter_mut().for_each(|dep| { - let real_pkg = patches.clone().find(|((_, fake_id), _)| *fake_id == dep.pkg); - if let Some((_, real)) = real_pkg { - dep.pkg = real; - } + if let Some(resolve) = res.resolve.as_mut() { + resolve.nodes.retain_mut(|node| { + // Replace `rustc-std-workspace` crate with the actual one in the dependency list + node.deps.iter_mut().for_each(|dep| { + let real_pkg = patches.clone().find(|((_, fake_id), _)| *fake_id == dep.pkg); + if let Some((_, real)) = real_pkg { + dep.pkg = real; + } + }); + // Remove this node if it's a fake one + !patches.clone().any(|((_, fake), _)| fake == node.id) }); - // Remove this node if it's a fake one - !patches.clone().any(|((_, fake), _)| fake == node.id) - }); + } // Remove the fake ones from the package list patches.map(|((idx, _), _)| idx).sorted().rev().for_each(|idx| { res.packages.remove(idx); diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 5620dfade2..c6be91229f 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -441,14 +441,15 @@ impl ProjectWorkspace { ) -> anyhow::Result { match &self.kind { ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } - | ProjectWorkspaceKind::Cargo { cargo, .. } => { + | ProjectWorkspaceKind::Cargo { cargo, .. } + if !cargo.no_deps() => + { WorkspaceBuildScripts::run_for_workspace(config, cargo, progress, &self.sysroot) .with_context(|| { format!("Failed to run build scripts for {}", cargo.workspace_root()) }) } - ProjectWorkspaceKind::DetachedFile { cargo: None, .. } - | ProjectWorkspaceKind::Json { .. } => Ok(WorkspaceBuildScripts::default()), + _ => Ok(WorkspaceBuildScripts::default()), } } diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs index 034c49c3d5..5f2871ac99 100644 --- a/crates/rust-analyzer/src/diagnostics.rs +++ b/crates/rust-analyzer/src/diagnostics.rs @@ -75,7 +75,7 @@ impl DiagnosticCollection { flycheck_id: usize, file_id: FileId, diagnostic: lsp_types::Diagnostic, - fix: Option, + fix: Option>, ) { let diagnostics = self.check.entry(flycheck_id).or_default().entry(file_id).or_default(); for existing_diagnostic in diagnostics.iter() { @@ -84,8 +84,10 @@ impl DiagnosticCollection { } } - let check_fixes = Arc::make_mut(&mut self.check_fixes); - check_fixes.entry(flycheck_id).or_default().entry(file_id).or_default().extend(fix); + if let Some(fix) = fix { + let check_fixes = Arc::make_mut(&mut self.check_fixes); + check_fixes.entry(flycheck_id).or_default().entry(file_id).or_default().push(*fix); + } diagnostics.push(diagnostic); self.changes.insert(file_id); } diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs index 208a70bc02..e330cfc3cf 100644 --- a/crates/rust-analyzer/src/diagnostics/to_proto.rs +++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs @@ -170,7 +170,7 @@ fn resolve_path( struct SubDiagnostic { related: lsp_types::DiagnosticRelatedInformation, - suggested_fix: Option, + suggested_fix: Option>, } enum MappedRustChildDiagnostic { @@ -241,7 +241,7 @@ fn map_rust_child_diagnostic( location: location(config, workspace_root, spans[0], snap), message: message.clone(), }, - suggested_fix: Some(Fix { + suggested_fix: Some(Box::new(Fix { ranges: spans .iter() .map(|&span| location(config, workspace_root, span, snap).range) @@ -260,7 +260,7 @@ fn map_rust_child_diagnostic( data: None, command: None, }, - }), + })), }) } } @@ -269,7 +269,7 @@ fn map_rust_child_diagnostic( pub(crate) struct MappedRustDiagnostic { pub(crate) url: lsp_types::Url, pub(crate) diagnostic: lsp_types::Diagnostic, - pub(crate) fix: Option, + pub(crate) fix: Option>, } /// Converts a Rust root diagnostic to LSP form diff --git a/crates/rust-analyzer/src/flycheck.rs b/crates/rust-analyzer/src/flycheck.rs index 8f2e7d1ca2..443f52c6dd 100644 --- a/crates/rust-analyzer/src/flycheck.rs +++ b/crates/rust-analyzer/src/flycheck.rs @@ -218,6 +218,7 @@ struct FlycheckActor { status: FlycheckStatus, } +#[allow(clippy::large_enum_variant)] enum Event { RequestStateChange(StateChange), CheckEvent(Option), diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index dee34b1b39..71ed287268 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -165,6 +165,18 @@ impl GlobalState { self.proc_macro_clients.iter().map(Some).chain(iter::repeat_with(|| None)); for (ws, proc_macro_client) in self.workspaces.iter().zip(proc_macro_clients) { + if matches!( + &ws.kind, + ProjectWorkspaceKind::Cargo { cargo, .. } | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _)), .. } + if cargo.no_deps() + ) { + status.health |= lsp_ext::Health::Warning; + format_to!( + message, + "Workspace `{}` has been queried without dependencies, connecting to crates.io might have failed.\n\n", + ws.manifest_or_root() + ); + } if let Some(err) = ws.sysroot.error() { status.health |= lsp_ext::Health::Warning; format_to!(