Add error context to failures in ra_project_model using anyhow crate (#3119)

Add error context to failures in ra_project_model using anyhow crate
This commit is contained in:
Adam Bratschi-Kaye 2020-02-13 11:10:50 +01:00 committed by GitHub
parent 39abac8c91
commit 6f2cab1368
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 59 additions and 20 deletions

1
Cargo.lock generated
View File

@ -1247,6 +1247,7 @@ dependencies = [
name = "ra_project_model" name = "ra_project_model"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow",
"cargo_metadata", "cargo_metadata",
"log", "log",
"ra_arena", "ra_arena",

View File

@ -19,3 +19,5 @@ ra_cfg = { path = "../ra_cfg" }
serde = { version = "1.0.89", features = ["derive"] } serde = { version = "1.0.89", features = ["derive"] }
serde_json = "1.0.39" serde_json = "1.0.39"
anyhow = "1.0.26"

View File

@ -2,14 +2,13 @@
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use cargo_metadata::{CargoOpt, MetadataCommand}; use cargo_metadata::{CargoOpt, MetadataCommand};
use ra_arena::{impl_arena_id, Arena, RawId}; use ra_arena::{impl_arena_id, Arena, RawId};
use ra_db::Edition; use ra_db::Edition;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use serde::Deserialize; use serde::Deserialize;
use crate::Result;
/// `CargoWorkspace` represents the logical structure of, well, a Cargo /// `CargoWorkspace` represents the logical structure of, well, a Cargo
/// workspace. It pretty closely mirrors `cargo metadata` output. /// workspace. It pretty closely mirrors `cargo metadata` output.
/// ///
@ -171,7 +170,9 @@ pub fn from_cargo_metadata(
if let Some(parent) = cargo_toml.parent() { if let Some(parent) = cargo_toml.parent() {
meta.current_dir(parent); meta.current_dir(parent);
} }
let meta = meta.exec().map_err(|e| format!("cargo metadata failed: {}", e))?; let meta = meta.exec().with_context(|| {
format!("Failed to run `cargo metadata --manifest-path {}`", cargo_toml.display())
})?;
let mut pkg_by_id = FxHashMap::default(); let mut pkg_by_id = FxHashMap::default();
let mut packages = Arena::default(); let mut packages = Arena::default();
let mut targets = Arena::default(); let mut targets = Arena::default();
@ -181,7 +182,9 @@ pub fn from_cargo_metadata(
for meta_pkg in meta.packages { for meta_pkg in meta.packages {
let cargo_metadata::Package { id, edition, name, manifest_path, .. } = meta_pkg; let cargo_metadata::Package { id, edition, name, manifest_path, .. } = meta_pkg;
let is_member = ws_members.contains(&id); let is_member = ws_members.contains(&id);
let edition = edition.parse::<Edition>()?; let edition = edition
.parse::<Edition>()
.with_context(|| format!("Failed to parse edition {}", edition))?;
let pkg = packages.alloc(PackageData { let pkg = packages.alloc(PackageData {
name, name,
manifest: manifest_path, manifest: manifest_path,

View File

@ -12,6 +12,7 @@
process::Command, process::Command,
}; };
use anyhow::{bail, Context, Result};
use ra_cfg::CfgOptions; use ra_cfg::CfgOptions;
use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId}; use ra_db::{CrateGraph, CrateId, CrateName, Edition, Env, FileId};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
@ -23,8 +24,6 @@
sysroot::Sysroot, sysroot::Sysroot,
}; };
pub type Result<T> = ::std::result::Result<T, Box<dyn Error + Send + Sync>>;
#[derive(Clone, PartialEq, Eq, Hash, Debug)] #[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct CargoTomlNotFoundError(pub PathBuf); pub struct CargoTomlNotFoundError(pub PathBuf);
@ -81,15 +80,36 @@ pub fn discover_with_sysroot(
) -> Result<ProjectWorkspace> { ) -> Result<ProjectWorkspace> {
match find_rust_project_json(path) { match find_rust_project_json(path) {
Some(json_path) => { Some(json_path) => {
let file = File::open(json_path)?; let file = File::open(&json_path)
.with_context(|| format!("Failed to open json file {}", json_path.display()))?;
let reader = BufReader::new(file); let reader = BufReader::new(file);
Ok(ProjectWorkspace::Json { project: from_reader(reader)? }) Ok(ProjectWorkspace::Json {
project: from_reader(reader).with_context(|| {
format!("Failed to deserialize json file {}", json_path.display())
})?,
})
} }
None => { None => {
let cargo_toml = find_cargo_toml(path)?; let cargo_toml = find_cargo_toml(path).with_context(|| {
let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)?; format!("Failed to find Cargo.toml for path {}", path.display())
let sysroot = })?;
if with_sysroot { Sysroot::discover(&cargo_toml)? } else { Sysroot::default() }; let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)
.with_context(|| {
format!(
"Failed to read Cargo metadata from Cargo.toml file {}",
cargo_toml.display()
)
})?;
let sysroot = if with_sysroot {
Sysroot::discover(&cargo_toml).with_context(|| {
format!(
"Failed to find sysroot for Cargo.toml file {}",
cargo_toml.display()
)
})?
} else {
Sysroot::default()
};
Ok(ProjectWorkspace::Cargo { cargo, sysroot }) Ok(ProjectWorkspace::Cargo { cargo, sysroot })
} }
} }
@ -403,11 +423,20 @@ pub fn get_rustc_cfg_options() -> CfgOptions {
} }
} }
match (|| -> Result<_> { match (|| -> Result<String> {
// `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here. // `cfg(test)` and `cfg(debug_assertion)` are handled outside, so we suppress them here.
let output = Command::new("rustc").args(&["--print", "cfg", "-O"]).output()?; let output = Command::new("rustc")
.args(&["--print", "cfg", "-O"])
.output()
.context("Failed to get output from rustc --print cfg -O")?;
if !output.status.success() { if !output.status.success() {
Err("failed to get rustc cfgs")?; bail!(
"rustc --print cfg -O exited with exit code ({})",
output
.status
.code()
.map_or(String::from("no exit code"), |code| format!("{}", code))
);
} }
Ok(String::from_utf8(output.stdout)?) Ok(String::from_utf8(output.stdout)?)
})() { })() {

View File

@ -1,5 +1,6 @@
//! FIXME: write short doc here //! FIXME: write short doc here
use anyhow::{anyhow, bail, Context, Result};
use std::{ use std::{
env, env,
path::{Path, PathBuf}, path::{Path, PathBuf},
@ -8,8 +9,6 @@
use ra_arena::{impl_arena_id, Arena, RawId}; use ra_arena::{impl_arena_id, Arena, RawId};
use crate::Result;
#[derive(Default, Debug, Clone)] #[derive(Default, Debug, Clone)]
pub struct Sysroot { pub struct Sysroot {
crates: Arena<SysrootCrate, SysrootCrateData>, crates: Arena<SysrootCrate, SysrootCrateData>,
@ -51,7 +50,7 @@ pub fn discover(cargo_toml: &Path) -> Result<Sysroot> {
let src = try_find_src_path(cargo_toml)?; let src = try_find_src_path(cargo_toml)?;
if !src.exists() { if !src.exists() {
Err(format!( Err(anyhow!(
"can't load standard library from sysroot\n\ "can't load standard library from sysroot\n\
{}\n\ {}\n\
(discovered via `rustc --print sysroot`)\n\ (discovered via `rustc --print sysroot`)\n\
@ -100,9 +99,14 @@ fn try_find_src_path(cargo_toml: &Path) -> Result<PathBuf> {
.current_dir(cargo_toml.parent().unwrap()) .current_dir(cargo_toml.parent().unwrap())
.args(&["--print", "sysroot"]) .args(&["--print", "sysroot"])
.output() .output()
.map_err(|e| format!("rustc --print sysroot failed: {}", e))?; .context("rustc --print sysroot failed")?;
if !rustc_output.status.success() { if !rustc_output.status.success() {
Err("failed to locate sysroot")?; match rustc_output.status.code() {
Some(code) => {
bail!("failed to locate sysroot: rustc --print sysroot exited with code {}", code)
}
None => bail!("failed to locate sysroot: rustc --print sysroot terminated by signal"),
};
} }
let stdout = String::from_utf8(rustc_output.stdout)?; let stdout = String::from_utf8(rustc_output.stdout)?;
let sysroot_path = Path::new(stdout.trim()); let sysroot_path = Path::new(stdout.trim());