diff --git a/src/analysis.rs b/src/analysis.rs index 8a48f40..68b0b0e 100644 --- a/src/analysis.rs +++ b/src/analysis.rs @@ -1,13 +1,16 @@ use rls_analysis::{AnalysisHost, AnalysisLoader, SearchDirectory}; use std::collections::btree_map::*; use std::convert::TryFrom; +use std::io::{stderr, Write}; use std::path::{Path, PathBuf}; +use std::process::Command; /// Write the analysis data to a subdirectory under target/ with this name. const SUBDIR: &str = "rsbrowse"; pub struct Analysis { pub crates: Vec, + pub stdlib_crates: Vec, } impl Analysis { @@ -24,7 +27,7 @@ impl Analysis { }) .expect("failed to json-serialize rust analysis configuration"); - let cargo_status = std::process::Command::new("cargo") + let cargo_status = Command::new("cargo") .arg("check") .arg("--target-dir") .arg(Path::new("target").join(SUBDIR)) @@ -51,8 +54,16 @@ impl Analysis { &loader, Default::default(), &[] as &[&str]) .into_iter() .map(|c| Crate::try_from(c).expect("unable to read crate analysis")) - .collect(); - Self { crates } + .collect::>(); + let mut stdlib_crates = vec![]; + if let Some(ref stdlib_base) = loader.stdlib_dir { + for krate in &crates { + if krate.inner.path.as_ref().unwrap().starts_with(stdlib_base) { + stdlib_crates.push(krate.id()); + } + } + } + Self { crates, stdlib_crates } } pub fn crate_ids<'a>(&'a self) -> impl Iterator + 'a { @@ -110,12 +121,13 @@ impl Analysis { index: id.index, }) } - self.get_crate(crate_id) + Some(self.try_get_crate(crate_id)? .inner .analysis .defs .iter() .find(|def| def.id == id) + .unwrap_or_else(|| panic!("invalid def ID {:?} for crate {:?}", id, crate_id))) } pub fn impls<'a>(&'a self, crate_id: &CrateId, parent_id: rls_data::Id) @@ -256,6 +268,7 @@ pub enum CrateType { Lib, ProcMacro, CDylib, + Dylib, } impl std::str::FromStr for CrateType { @@ -266,6 +279,7 @@ impl std::str::FromStr for CrateType { "lib" => Self::Lib, "proc-macro" => Self::ProcMacro, "cdylib" => Self::CDylib, + "dylib" => Self::Dylib, _ => { return Err(format!("unknown crate type {:?}", s)); } @@ -276,6 +290,7 @@ impl std::str::FromStr for CrateType { #[derive(Clone)] struct Loader { deps_dir: PathBuf, + stdlib_dir: Option, } impl AnalysisLoader for Loader { @@ -296,19 +311,59 @@ impl AnalysisLoader for Loader { } fn search_directories(&self) -> Vec { - vec![SearchDirectory { path : self.deps_dir.clone(), prefix_rewrite: None }] + let mut paths = vec![ + SearchDirectory { path : self.deps_dir.clone(), prefix_rewrite: None } + ]; + if let Some(path) = self.stdlib_dir.clone() { + paths.push(SearchDirectory { path, prefix_rewrite: None }); + } + paths } } impl Loader { pub fn new(path: impl Into, target: &str) -> Self { + let deps_dir = path.into() + .join("target") + .join(SUBDIR) + .join(target) + .join("deps") + .join("save-analysis"); + Self { - deps_dir: path.into() - .join("target") - .join(SUBDIR) - .join(target) - .join("deps") - .join("save-analysis"), + deps_dir, + stdlib_dir: get_stdlib_analysis_path(), } } } + +fn get_stdlib_analysis_path() -> Option { + Command::new("rustc") + .arg("--print") + .arg("target-libdir") + .output() + .map_err(|e| { + eprintln!("Error running 'rustc --print target-libdir': {}", e); + e + }) + .ok() + .and_then(|out| { + if out.status.success() { + let path = String::from_utf8(out.stdout) + .map_err(|e| { + eprintln!("'rustc --print target-libdir' returned invalid utf8: {}", e); + e + }) + .ok()?; + + Some(PathBuf::from(path.trim_end()) + .join("..") + .join("analysis")) + } else { + eprintln!("Error running 'rustc --print target-libdir': {}", out.status); + eprint!("Command stderr: "); + stderr().write_all(&out.stderr).unwrap(); + None + } + }) +} diff --git a/src/browser.rs b/src/browser.rs index bdf1062..79803fc 100644 --- a/src/browser.rs +++ b/src/browser.rs @@ -15,6 +15,7 @@ impl Browser { pub fn list_crates(&self) -> Vec<(String, CrateId)> { let mut crates = self.analysis.crate_ids() + .filter(|c| !self.analysis.stdlib_crates.contains(c)) .map(|c| (crate_label(&c), c)) .collect::>(); @@ -23,6 +24,24 @@ impl Browser { crates } + fn get_maybe_external_trait<'a>(&'a self, crate_id: &'a CrateId, trait_id: rls_data::Id) + -> (bool, &'a CrateId, rls_data::Id) + { + if trait_id.krate != 0 { + ( + true, + self.analysis.get_external_crate_id(crate_id, trait_id) + .expect("nonexistent external crate"), + rls_data::Id { + krate: 0, + index: trait_id.index, + } + ) + } else { + (false, crate_id, trait_id) + } + } + pub fn list_items(&self, crate_id: &CrateId, parent: &Item) -> Vec<(String, Item)> { let mut items = vec![]; match parent { @@ -41,36 +60,27 @@ impl Browser { if let Some(id) = parent_id { let mut impls = self.analysis.impls(crate_id, id) - .filter_map(|impl_details| { + .map(|impl_details| { let trait_name = match impl_details.trait_id { - Some(mut trait_id) => { - let mut trait_crate = crate_id; - let mut is_external_crate = false; - if trait_id.krate != parent_id.unwrap().krate { - let other = self.analysis.get_external_crate_id(crate_id, trait_id) - .expect("nonexistent external crate"); - trait_id.krate = 0; // now we're looking inside this one - trait_crate = other; - is_external_crate = true; - } - if self.analysis.try_get_crate(trait_crate).is_none() { - // Probably a reference to a trait in core or std, which we - // currently don't have a way to load analysis data for. - return None; - } - let trait_name = self.analysis.get_def(trait_crate, trait_id) - .expect("invalid trait ID") - .qualname - .clone(); - if is_external_crate { - trait_crate.name.clone() + &trait_name - } else { - trait_name - } + Some(trait_id) => { + let (is_external, trait_crate, trait_id) + = self.get_maybe_external_trait(crate_id, trait_id); + self.analysis.get_def(trait_crate, trait_id) + .map(|t| if is_external { + trait_crate.name.clone() + &t.qualname + } else { + t.qualname.clone() + }) + .unwrap_or_else(|| { + format!("{}::(unresolved trait at {}:{})", + trait_crate.name, + impl_details.span.file_name.display(), + impl_details.span.line_start.0) + }) } None => "Self".to_owned(), }; - Some((format!("impl {}", trait_name), Item::Impl(impl_details))) + (format!("impl {}", trait_name), Item::Impl(impl_details)) }) .collect::>(); @@ -94,27 +104,22 @@ impl Browser { } // Trait methods. - if let Some(mut trait_id) = impl_details.trait_id { - let mut trait_crate_id = crate_id; - let mut is_external = false; - if trait_id.krate != 0 { - trait_crate_id = self.analysis.get_external_crate_id(crate_id, trait_id) - .expect("nonexistent external crate"); - trait_id.krate = 0; - is_external = true; - } + if let Some(trait_id) = impl_details.trait_id { + let (is_external, trait_crate, trait_id) = self.get_maybe_external_trait( + crate_id, trait_id); - let def = self.analysis.get_def(&trait_crate_id, trait_id) - .expect("no such trait"); + let children = self.analysis.get_def(&trait_crate, trait_id) + .map(|def| &def.children[..]) + .unwrap_or(&[]); - for id in &def.children { - if let Some(method) = self.analysis.get_def(&trait_crate_id, *id) { + for id in children { + if let Some(method) = self.analysis.get_def(&trait_crate, *id) { // Add to the map only if not existing (if it already exists it means // the method has been overridden). methods.entry(def_label(method)) .or_insert_with(|| { if is_external { - Item::ExternalDef(trait_crate_id.to_owned(), method.clone()) + Item::ExternalDef(trait_crate.to_owned(), method.clone()) } else { Item::Def(method.clone()) } @@ -159,8 +164,11 @@ impl Browser { } Item::Impl(imp) => { if let Some(t) = imp.trait_id { - let tdef = self.analysis.get_def(crate_id, t).unwrap(); - txt += &format!("implementation of trait {}", tdef.qualname); + if let Some(tdef) = self.analysis.get_def(crate_id, t) { + txt += &format!("implementation of trait {}", tdef.qualname); + } else { + txt += "implementation of unresolved trait"; + } } else { txt += "inherent impl"; // nothing else to show really @@ -215,6 +223,7 @@ fn crate_label(c: &CrateId) -> String { CrateType::ProcMacro => format!("{} (proc-macro)", c.name), CrateType::Lib => c.name.clone(), CrateType::CDylib => format!("{} (cdylib)", c.name), + CrateType::Dylib => format!("{} (dylib)", c.name), } } diff --git a/src/ui.rs b/src/ui.rs index 7d6c9ae..6fd208d 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -120,7 +120,7 @@ fn get_source_for_def(def: &rls_data::Def) -> (String, usize) { (txt, line as usize) } Err(e) => { - return (format!("error opening source: {}", e), 0); + (format!("error opening source: {}", e), 0) } } } diff --git a/tests/testlib.rs b/tests/testlib.rs index d0f5a84..d8847e2 100644 --- a/tests/testlib.rs +++ b/tests/testlib.rs @@ -145,7 +145,11 @@ fn list_items() { let x_s = mod_x_items.by_label("struct S"); let x_s_items = BROWSER.list_items(crate_id, x_s); - assert_eq!(x_s_items.labels(), &["impl Self", "impl externcrate::ExternTrait"]); + assert_eq!(x_s_items.labels(), &[ + "impl Self", + "impl core::fmt::Display", + "impl externcrate::ExternTrait", + ]); let y_s = mod_y_items.by_label("struct S"); let y_s_items = BROWSER.list_items(crate_id, y_s);