From e8923713c51bc3484bd98085ad620713959bbc0d Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 10 Jan 2019 20:13:08 +0300 Subject: add sysroot boilerplate --- crates/ra_lsp_server/src/main_loop/handlers.rs | 8 +- crates/ra_lsp_server/src/project_model.rs | 116 ++++++++++++++++++--- .../src/project_model/cargo_workspace.rs | 0 crates/ra_lsp_server/src/project_model/sysroot.rs | 0 crates/ra_lsp_server/src/server_world.rs | 24 ++--- crates/ra_lsp_server/tests/heavy_tests/main.rs | 41 +++++++- 6 files changed, 157 insertions(+), 32 deletions(-) create mode 100644 crates/ra_lsp_server/src/project_model/cargo_workspace.rs create mode 100644 crates/ra_lsp_server/src/project_model/sysroot.rs diff --git a/crates/ra_lsp_server/src/main_loop/handlers.rs b/crates/ra_lsp_server/src/main_loop/handlers.rs index 069e7f932..0dda9548a 100644 --- a/crates/ra_lsp_server/src/main_loop/handlers.rs +++ b/crates/ra_lsp_server/src/main_loop/handlers.rs @@ -347,11 +347,11 @@ pub fn handle_runnables( .read() .file2path(ra_vfs::VfsFile(file_id.0.into())); let res = world.workspaces.iter().find_map(|ws| { - let tgt = ws.target_by_root(&path)?; + let tgt = ws.cargo.target_by_root(&path)?; let res = CargoTargetSpec { - package: tgt.package(ws).name(ws).to_string(), - target: tgt.name(ws).to_string(), - target_kind: tgt.kind(ws), + package: tgt.package(&ws.cargo).name(&ws.cargo).to_string(), + target: tgt.name(&ws.cargo).to_string(), + target_kind: tgt.kind(&ws.cargo), }; Some(res) }); diff --git a/crates/ra_lsp_server/src/project_model.rs b/crates/ra_lsp_server/src/project_model.rs index 9f429c9a1..6fbaba7d9 100644 --- a/crates/ra_lsp_server/src/project_model.rs +++ b/crates/ra_lsp_server/src/project_model.rs @@ -1,4 +1,7 @@ -use std::path::{Path, PathBuf}; +use std::{ + path::{Path, PathBuf}, + process::Command, +}; use cargo_metadata::{metadata_run, CargoOpt}; use ra_syntax::SmolStr; @@ -9,6 +12,36 @@ use thread_worker::{WorkerHandle, Worker}; use crate::Result; +#[derive(Debug, Clone)] +pub struct ProjectWorkspace { + pub(crate) cargo: CargoWorkspace, + pub(crate) sysroot: Sysroot, +} + +impl ProjectWorkspace { + pub fn discover(path: &Path) -> Result { + let cargo_toml = find_cargo_toml(path)?; + let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml)?; + let sysroot = sysroot_info(&cargo_toml)?; + let res = ProjectWorkspace { cargo, sysroot }; + Ok(res) + } +} + +pub fn workspace_loader() -> (Worker>, WorkerHandle) { + thread_worker::spawn::, _>( + "workspace loader", + 1, + |input_receiver, output_sender| { + input_receiver + .into_iter() + .map(|path| ProjectWorkspace::discover(path.as_path())) + .try_for_each(|it| output_sender.send(it)) + .unwrap() + }, + ) +} + /// `CargoWorksapce` represents the logical structure of, well, a Cargo /// workspace. It pretty closely mirrors `cargo metadata` output. /// @@ -63,6 +96,11 @@ pub enum TargetKind { Other, } +#[derive(Debug, Clone)] +pub(crate) struct Sysroot { + crates: FxHashMap, +} + impl Package { pub fn name(self, ws: &CargoWorkspace) -> &str { ws.packages[self].name.as_str() @@ -160,6 +198,68 @@ impl CargoWorkspace { } } +fn sysroot_info(cargo_toml: &Path) -> Result { + let rustc_output = Command::new("rustc") + .current_dir(cargo_toml.parent().unwrap()) + .args(&["--print", "sysroot"]) + .output()?; + if !rustc_output.status.success() { + failure::bail!("failed to locate sysroot") + } + let stdout = String::from_utf8(rustc_output.stdout)?; + let sysroot_path = Path::new(stdout.trim()); + let src = sysroot_path.join("lib/rustlib/src/rust/src"); + + let crates: &[(&str, &[&str])] = &[ + ( + "std", + &[ + "alloc_jemalloc", + "alloc_system", + "panic_abort", + "rand", + "compiler_builtins", + "unwind", + "rustc_asan", + "rustc_lsan", + "rustc_msan", + "rustc_tsan", + "build_helper", + ], + ), + ("core", &[]), + ("alloc", &[]), + ("collections", &[]), + ("libc", &[]), + ("panic_unwind", &[]), + ("proc_macro", &[]), + ("rustc_unicode", &[]), + ("std_unicode", &[]), + ("test", &[]), + // Feature gated + ("alloc_jemalloc", &[]), + ("alloc_system", &[]), + ("compiler_builtins", &[]), + ("getopts", &[]), + ("panic_unwind", &[]), + ("panic_abort", &[]), + ("rand", &[]), + ("term", &[]), + ("unwind", &[]), + // Dependencies + ("build_helper", &[]), + ("rustc_asan", &[]), + ("rustc_lsan", &[]), + ("rustc_msan", &[]), + ("rustc_tsan", &[]), + ("syntax", &[]), + ]; + + Ok(Sysroot { + crates: FxHashMap::default(), + }) +} + fn find_cargo_toml(path: &Path) -> Result { if path.ends_with("Cargo.toml") { return Ok(path.to_path_buf()); @@ -190,17 +290,3 @@ impl TargetKind { TargetKind::Other } } - -pub fn workspace_loader() -> (Worker>, WorkerHandle) { - thread_worker::spawn::, _>( - "workspace loader", - 1, - |input_receiver, output_sender| { - input_receiver - .into_iter() - .map(|path| CargoWorkspace::from_cargo_metadata(path.as_path())) - .try_for_each(|it| output_sender.send(it)) - .unwrap() - }, - ) -} diff --git a/crates/ra_lsp_server/src/project_model/cargo_workspace.rs b/crates/ra_lsp_server/src/project_model/cargo_workspace.rs new file mode 100644 index 000000000..e69de29bb diff --git a/crates/ra_lsp_server/src/project_model/sysroot.rs b/crates/ra_lsp_server/src/project_model/sysroot.rs new file mode 100644 index 000000000..e69de29bb diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs index 76c76766d..2debbe557 100644 --- a/crates/ra_lsp_server/src/server_world.rs +++ b/crates/ra_lsp_server/src/server_world.rs @@ -15,7 +15,7 @@ use parking_lot::RwLock; use failure::format_err; use crate::{ - project_model::{CargoWorkspace, TargetKind}, + project_model::{ProjectWorkspace, TargetKind}, Result, }; @@ -23,26 +23,26 @@ use crate::{ pub struct ServerWorldState { pub roots_to_scan: usize, pub root: PathBuf, - pub workspaces: Arc>, + pub workspaces: Arc>, pub analysis_host: AnalysisHost, pub vfs: Arc>, } pub struct ServerWorld { - pub workspaces: Arc>, + pub workspaces: Arc>, pub analysis: Analysis, pub vfs: Arc>, } impl ServerWorldState { - pub fn new(root: PathBuf, workspaces: Vec) -> ServerWorldState { + pub fn new(root: PathBuf, workspaces: Vec) -> ServerWorldState { let mut change = AnalysisChange::new(); let mut roots = Vec::new(); roots.push(root.clone()); for ws in workspaces.iter() { - for pkg in ws.packages() { - roots.push(pkg.root(&ws).to_path_buf()); + for pkg in ws.cargo.packages() { + roots.push(pkg.root(&ws.cargo).to_path_buf()); } } let roots_to_scan = roots.len(); @@ -56,13 +56,13 @@ impl ServerWorldState { let mut pkg_to_lib_crate = FxHashMap::default(); let mut pkg_crates = FxHashMap::default(); for ws in workspaces.iter() { - for pkg in ws.packages() { - for tgt in pkg.targets(ws) { - let root = tgt.root(ws); + for pkg in ws.cargo.packages() { + for tgt in pkg.targets(&ws.cargo) { + let root = tgt.root(&ws.cargo); if let Some(file_id) = vfs.load(root) { let file_id = FileId(file_id.0.into()); let crate_id = crate_graph.add_crate_root(file_id); - if tgt.kind(ws) == TargetKind::Lib { + if tgt.kind(&ws.cargo) == TargetKind::Lib { pkg_to_lib_crate.insert(pkg, crate_id); } pkg_crates @@ -72,8 +72,8 @@ impl ServerWorldState { } } } - for pkg in ws.packages() { - for dep in pkg.dependencies(ws) { + for pkg in ws.cargo.packages() { + for dep in pkg.dependencies(&ws.cargo) { if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { for &from in pkg_crates.get(&pkg).into_iter().flatten() { crate_graph.add_dep(from, dep.name.clone(), to); diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs index 9eaf46ac8..927664ffb 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/main.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs @@ -4,7 +4,7 @@ use languageserver_types::{ CodeActionContext, DocumentFormattingParams, FormattingOptions, Position, Range, }; use ra_lsp_server::req::{ - CodeActionParams, CodeActionRequest, Formatting, Runnables, RunnablesParams, + CodeActionParams, CodeActionRequest, Formatting, Runnables, RunnablesParams, CompletionParams, Completion, }; use serde_json::json; @@ -12,6 +12,45 @@ use crate::support::project; const LOG: &'static str = ""; +#[test] +fn completes_items_from_standard_library() { + let server = project( + r#" +//- Cargo.toml +[package] +name = "foo" +version = "0.0.0" + +//- src/lib.rs +use std::collections::; +"#, + ); + server.wait_for_feedback("workspace loaded"); + server.request::( + CompletionParams { + text_document: server.doc_id("src/lib.rs"), + context: None, + position: Position::new(0, 22), + }, + json!([ + { + "filterText": "self", + "insertText": "self", + "insertTextFormat": 1, + "kind": 14, + "label": "self" + }, + { + "filterText": "super", + "insertText": "super", + "insertTextFormat": 1, + "kind": 14, + "label": "super" + } + ]), + ); +} + #[test] fn test_runnables_no_project() { let server = project( -- cgit v1.2.3 From 66fba88534039ff42a230f1ede3e0a730f61ad3c Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 10 Jan 2019 22:21:14 +0300 Subject: split module --- crates/ra_lsp_server/src/project_model.rs | 251 +-------------------- .../src/project_model/cargo_workspace.rs | 173 ++++++++++++++ crates/ra_lsp_server/src/project_model/sysroot.rs | 78 +++++++ 3 files changed, 261 insertions(+), 241 deletions(-) diff --git a/crates/ra_lsp_server/src/project_model.rs b/crates/ra_lsp_server/src/project_model.rs index 6fbaba7d9..3d02c6549 100644 --- a/crates/ra_lsp_server/src/project_model.rs +++ b/crates/ra_lsp_server/src/project_model.rs @@ -1,17 +1,20 @@ +mod cargo_workspace; +mod sysroot; + use std::{ path::{Path, PathBuf}, - process::Command, }; -use cargo_metadata::{metadata_run, CargoOpt}; -use ra_syntax::SmolStr; -use ra_arena::{Arena, RawId, impl_arena_id}; -use rustc_hash::FxHashMap; -use failure::{format_err, bail}; +use failure::bail; use thread_worker::{WorkerHandle, Worker}; use crate::Result; +pub use crate::project_model::{ + cargo_workspace::{CargoWorkspace, Package, Target, TargetKind}, + sysroot::Sysroot, +}; + #[derive(Debug, Clone)] pub struct ProjectWorkspace { pub(crate) cargo: CargoWorkspace, @@ -22,7 +25,7 @@ impl ProjectWorkspace { pub fn discover(path: &Path) -> Result { let cargo_toml = find_cargo_toml(path)?; let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml)?; - let sysroot = sysroot_info(&cargo_toml)?; + let sysroot = Sysroot::discover(&cargo_toml)?; let res = ProjectWorkspace { cargo, sysroot }; Ok(res) } @@ -42,224 +45,6 @@ pub fn workspace_loader() -> (Worker>, WorkerH ) } -/// `CargoWorksapce` represents the logical structure of, well, a Cargo -/// workspace. It pretty closely mirrors `cargo metadata` output. -/// -/// Note that internally, rust analyzer uses a differnet structure: -/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates, -/// while this knows about `Pacakges` & `Targets`: purely cargo-related -/// concepts. -#[derive(Debug, Clone)] -pub struct CargoWorkspace { - packages: Arena, - targets: Arena, -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct Package(RawId); -impl_arena_id!(Package); - -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct Target(RawId); -impl_arena_id!(Target); - -#[derive(Debug, Clone)] -struct PackageData { - name: SmolStr, - manifest: PathBuf, - targets: Vec, - is_member: bool, - dependencies: Vec, -} - -#[derive(Debug, Clone)] -pub struct PackageDependency { - pub pkg: Package, - pub name: SmolStr, -} - -#[derive(Debug, Clone)] -struct TargetData { - pkg: Package, - name: SmolStr, - root: PathBuf, - kind: TargetKind, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum TargetKind { - Bin, - Lib, - Example, - Test, - Bench, - Other, -} - -#[derive(Debug, Clone)] -pub(crate) struct Sysroot { - crates: FxHashMap, -} - -impl Package { - pub fn name(self, ws: &CargoWorkspace) -> &str { - ws.packages[self].name.as_str() - } - pub fn root(self, ws: &CargoWorkspace) -> &Path { - ws.packages[self].manifest.parent().unwrap() - } - pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator + 'a { - ws.packages[self].targets.iter().cloned() - } - #[allow(unused)] - pub fn is_member(self, ws: &CargoWorkspace) -> bool { - ws.packages[self].is_member - } - pub fn dependencies<'a>( - self, - ws: &'a CargoWorkspace, - ) -> impl Iterator + 'a { - ws.packages[self].dependencies.iter() - } -} - -impl Target { - pub fn package(self, ws: &CargoWorkspace) -> Package { - ws.targets[self].pkg - } - pub fn name(self, ws: &CargoWorkspace) -> &str { - ws.targets[self].name.as_str() - } - pub fn root(self, ws: &CargoWorkspace) -> &Path { - ws.targets[self].root.as_path() - } - pub fn kind(self, ws: &CargoWorkspace) -> TargetKind { - ws.targets[self].kind - } -} - -impl CargoWorkspace { - pub fn from_cargo_metadata(path: &Path) -> Result { - let cargo_toml = find_cargo_toml(path)?; - let meta = metadata_run( - Some(cargo_toml.as_path()), - true, - Some(CargoOpt::AllFeatures), - ) - .map_err(|e| format_err!("cargo metadata failed: {}", e))?; - let mut pkg_by_id = FxHashMap::default(); - let mut packages = Arena::default(); - let mut targets = Arena::default(); - - let ws_members = &meta.workspace_members; - - for meta_pkg in meta.packages { - let is_member = ws_members.contains(&meta_pkg.id); - let pkg = packages.alloc(PackageData { - name: meta_pkg.name.into(), - manifest: meta_pkg.manifest_path.clone(), - targets: Vec::new(), - is_member, - dependencies: Vec::new(), - }); - let pkg_data = &mut packages[pkg]; - pkg_by_id.insert(meta_pkg.id.clone(), pkg); - for meta_tgt in meta_pkg.targets { - let tgt = targets.alloc(TargetData { - pkg, - name: meta_tgt.name.into(), - root: meta_tgt.src_path.clone(), - kind: TargetKind::new(meta_tgt.kind.as_slice()), - }); - pkg_data.targets.push(tgt); - } - } - let resolve = meta.resolve.expect("metadata executed with deps"); - for node in resolve.nodes { - let source = pkg_by_id[&node.id]; - for dep_node in node.deps { - let dep = PackageDependency { - name: dep_node.name.into(), - pkg: pkg_by_id[&dep_node.pkg], - }; - packages[source].dependencies.push(dep); - } - } - - Ok(CargoWorkspace { packages, targets }) - } - pub fn packages<'a>(&'a self) -> impl Iterator + 'a { - self.packages.iter().map(|(id, _pkg)| id) - } - pub fn target_by_root(&self, root: &Path) -> Option { - self.packages() - .filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root)) - .next() - } -} - -fn sysroot_info(cargo_toml: &Path) -> Result { - let rustc_output = Command::new("rustc") - .current_dir(cargo_toml.parent().unwrap()) - .args(&["--print", "sysroot"]) - .output()?; - if !rustc_output.status.success() { - failure::bail!("failed to locate sysroot") - } - let stdout = String::from_utf8(rustc_output.stdout)?; - let sysroot_path = Path::new(stdout.trim()); - let src = sysroot_path.join("lib/rustlib/src/rust/src"); - - let crates: &[(&str, &[&str])] = &[ - ( - "std", - &[ - "alloc_jemalloc", - "alloc_system", - "panic_abort", - "rand", - "compiler_builtins", - "unwind", - "rustc_asan", - "rustc_lsan", - "rustc_msan", - "rustc_tsan", - "build_helper", - ], - ), - ("core", &[]), - ("alloc", &[]), - ("collections", &[]), - ("libc", &[]), - ("panic_unwind", &[]), - ("proc_macro", &[]), - ("rustc_unicode", &[]), - ("std_unicode", &[]), - ("test", &[]), - // Feature gated - ("alloc_jemalloc", &[]), - ("alloc_system", &[]), - ("compiler_builtins", &[]), - ("getopts", &[]), - ("panic_unwind", &[]), - ("panic_abort", &[]), - ("rand", &[]), - ("term", &[]), - ("unwind", &[]), - // Dependencies - ("build_helper", &[]), - ("rustc_asan", &[]), - ("rustc_lsan", &[]), - ("rustc_msan", &[]), - ("rustc_tsan", &[]), - ("syntax", &[]), - ]; - - Ok(Sysroot { - crates: FxHashMap::default(), - }) -} - fn find_cargo_toml(path: &Path) -> Result { if path.ends_with("Cargo.toml") { return Ok(path.to_path_buf()); @@ -274,19 +59,3 @@ fn find_cargo_toml(path: &Path) -> Result { } bail!("can't find Cargo.toml at {}", path.display()) } - -impl TargetKind { - fn new(kinds: &[String]) -> TargetKind { - for kind in kinds { - return match kind.as_str() { - "bin" => TargetKind::Bin, - "test" => TargetKind::Test, - "bench" => TargetKind::Bench, - "example" => TargetKind::Example, - _ if kind.contains("lib") => TargetKind::Lib, - _ => continue, - }; - } - TargetKind::Other - } -} diff --git a/crates/ra_lsp_server/src/project_model/cargo_workspace.rs b/crates/ra_lsp_server/src/project_model/cargo_workspace.rs index e69de29bb..138fdee22 100644 --- a/crates/ra_lsp_server/src/project_model/cargo_workspace.rs +++ b/crates/ra_lsp_server/src/project_model/cargo_workspace.rs @@ -0,0 +1,173 @@ +use std::{ + path::{Path, PathBuf}, +}; + +use cargo_metadata::{metadata_run, CargoOpt}; +use ra_syntax::SmolStr; +use ra_arena::{Arena, RawId, impl_arena_id}; +use rustc_hash::FxHashMap; +use failure::format_err; + +use crate::Result; + +/// `CargoWorksapce` represents the logical structure of, well, a Cargo +/// workspace. It pretty closely mirrors `cargo metadata` output. +/// +/// Note that internally, rust analyzer uses a differnet structure: +/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates, +/// while this knows about `Pacakges` & `Targets`: purely cargo-related +/// concepts. +#[derive(Debug, Clone)] +pub struct CargoWorkspace { + packages: Arena, + targets: Arena, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct Package(RawId); +impl_arena_id!(Package); + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub struct Target(RawId); +impl_arena_id!(Target); + +#[derive(Debug, Clone)] +struct PackageData { + name: SmolStr, + manifest: PathBuf, + targets: Vec, + is_member: bool, + dependencies: Vec, +} + +#[derive(Debug, Clone)] +pub struct PackageDependency { + pub pkg: Package, + pub name: SmolStr, +} + +#[derive(Debug, Clone)] +struct TargetData { + pkg: Package, + name: SmolStr, + root: PathBuf, + kind: TargetKind, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TargetKind { + Bin, + Lib, + Example, + Test, + Bench, + Other, +} + +impl TargetKind { + fn new(kinds: &[String]) -> TargetKind { + for kind in kinds { + return match kind.as_str() { + "bin" => TargetKind::Bin, + "test" => TargetKind::Test, + "bench" => TargetKind::Bench, + "example" => TargetKind::Example, + _ if kind.contains("lib") => TargetKind::Lib, + _ => continue, + }; + } + TargetKind::Other + } +} + +impl Package { + pub fn name(self, ws: &CargoWorkspace) -> &str { + ws.packages[self].name.as_str() + } + pub fn root(self, ws: &CargoWorkspace) -> &Path { + ws.packages[self].manifest.parent().unwrap() + } + pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator + 'a { + ws.packages[self].targets.iter().cloned() + } + #[allow(unused)] + pub fn is_member(self, ws: &CargoWorkspace) -> bool { + ws.packages[self].is_member + } + pub fn dependencies<'a>( + self, + ws: &'a CargoWorkspace, + ) -> impl Iterator + 'a { + ws.packages[self].dependencies.iter() + } +} + +impl Target { + pub fn package(self, ws: &CargoWorkspace) -> Package { + ws.targets[self].pkg + } + pub fn name(self, ws: &CargoWorkspace) -> &str { + ws.targets[self].name.as_str() + } + pub fn root(self, ws: &CargoWorkspace) -> &Path { + ws.targets[self].root.as_path() + } + pub fn kind(self, ws: &CargoWorkspace) -> TargetKind { + ws.targets[self].kind + } +} + +impl CargoWorkspace { + pub fn from_cargo_metadata(cargo_toml: &Path) -> Result { + let meta = metadata_run(Some(cargo_toml), true, Some(CargoOpt::AllFeatures)) + .map_err(|e| format_err!("cargo metadata failed: {}", e))?; + let mut pkg_by_id = FxHashMap::default(); + let mut packages = Arena::default(); + let mut targets = Arena::default(); + + let ws_members = &meta.workspace_members; + + for meta_pkg in meta.packages { + let is_member = ws_members.contains(&meta_pkg.id); + let pkg = packages.alloc(PackageData { + name: meta_pkg.name.into(), + manifest: meta_pkg.manifest_path.clone(), + targets: Vec::new(), + is_member, + dependencies: Vec::new(), + }); + let pkg_data = &mut packages[pkg]; + pkg_by_id.insert(meta_pkg.id.clone(), pkg); + for meta_tgt in meta_pkg.targets { + let tgt = targets.alloc(TargetData { + pkg, + name: meta_tgt.name.into(), + root: meta_tgt.src_path.clone(), + kind: TargetKind::new(meta_tgt.kind.as_slice()), + }); + pkg_data.targets.push(tgt); + } + } + let resolve = meta.resolve.expect("metadata executed with deps"); + for node in resolve.nodes { + let source = pkg_by_id[&node.id]; + for dep_node in node.deps { + let dep = PackageDependency { + name: dep_node.name.into(), + pkg: pkg_by_id[&dep_node.pkg], + }; + packages[source].dependencies.push(dep); + } + } + + Ok(CargoWorkspace { packages, targets }) + } + pub fn packages<'a>(&'a self) -> impl Iterator + 'a { + self.packages.iter().map(|(id, _pkg)| id) + } + pub fn target_by_root(&self, root: &Path) -> Option { + self.packages() + .filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root)) + .next() + } +} diff --git a/crates/ra_lsp_server/src/project_model/sysroot.rs b/crates/ra_lsp_server/src/project_model/sysroot.rs index e69de29bb..ae72c9c17 100644 --- a/crates/ra_lsp_server/src/project_model/sysroot.rs +++ b/crates/ra_lsp_server/src/project_model/sysroot.rs @@ -0,0 +1,78 @@ +use std::{ + path::{Path, PathBuf}, + process::Command, +}; + +use ra_syntax::SmolStr; +use rustc_hash::FxHashMap; + +use crate::Result; + +#[derive(Debug, Clone)] +pub struct Sysroot { + crates: FxHashMap, +} + +impl Sysroot { + pub(crate) fn discover(cargo_toml: &Path) -> Result { + let rustc_output = Command::new("rustc") + .current_dir(cargo_toml.parent().unwrap()) + .args(&["--print", "sysroot"]) + .output()?; + if !rustc_output.status.success() { + failure::bail!("failed to locate sysroot") + } + let stdout = String::from_utf8(rustc_output.stdout)?; + let sysroot_path = Path::new(stdout.trim()); + let src = sysroot_path.join("lib/rustlib/src/rust/src"); + + let crates: &[(&str, &[&str])] = &[ + ( + "std", + &[ + "alloc_jemalloc", + "alloc_system", + "panic_abort", + "rand", + "compiler_builtins", + "unwind", + "rustc_asan", + "rustc_lsan", + "rustc_msan", + "rustc_tsan", + "build_helper", + ], + ), + ("core", &[]), + ("alloc", &[]), + ("collections", &[]), + ("libc", &[]), + ("panic_unwind", &[]), + ("proc_macro", &[]), + ("rustc_unicode", &[]), + ("std_unicode", &[]), + ("test", &[]), + // Feature gated + ("alloc_jemalloc", &[]), + ("alloc_system", &[]), + ("compiler_builtins", &[]), + ("getopts", &[]), + ("panic_unwind", &[]), + ("panic_abort", &[]), + ("rand", &[]), + ("term", &[]), + ("unwind", &[]), + // Dependencies + ("build_helper", &[]), + ("rustc_asan", &[]), + ("rustc_lsan", &[]), + ("rustc_msan", &[]), + ("rustc_tsan", &[]), + ("syntax", &[]), + ]; + + Ok(Sysroot { + crates: FxHashMap::default(), + }) + } +} -- cgit v1.2.3 From 8852408bfb358766d59b83f294148fb5eeae26a0 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 10 Jan 2019 22:47:05 +0300 Subject: use arena for sysroot --- crates/ra_arena/src/lib.rs | 11 +- crates/ra_lsp_server/src/project_model/sysroot.rs | 129 ++++++++++++++-------- 2 files changed, 90 insertions(+), 50 deletions(-) diff --git a/crates/ra_arena/src/lib.rs b/crates/ra_arena/src/lib.rs index 43bfa925a..d7d5d5265 100644 --- a/crates/ra_arena/src/lib.rs +++ b/crates/ra_arena/src/lib.rs @@ -35,12 +35,21 @@ impl fmt::Display for RawId { } } -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq)] pub struct Arena { data: Vec, _ty: PhantomData, } +impl fmt::Debug for Arena { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("Arena") + .field("len", &self.len()) + .field("data", &self.data) + .finish() + } +} + #[macro_export] macro_rules! impl_arena_id { ($name:ident) => { diff --git a/crates/ra_lsp_server/src/project_model/sysroot.rs b/crates/ra_lsp_server/src/project_model/sysroot.rs index ae72c9c17..6c1a1a2a3 100644 --- a/crates/ra_lsp_server/src/project_model/sysroot.rs +++ b/crates/ra_lsp_server/src/project_model/sysroot.rs @@ -4,13 +4,24 @@ use std::{ }; use ra_syntax::SmolStr; -use rustc_hash::FxHashMap; +use ra_arena::{Arena, RawId, impl_arena_id}; use crate::Result; #[derive(Debug, Clone)] pub struct Sysroot { - crates: FxHashMap, + crates: Arena, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct SysrootCrate(RawId); +impl_arena_id!(SysrootCrate); + +#[derive(Debug, Clone)] +struct SysrootCrateData { + name: SmolStr, + path: PathBuf, + deps: Vec, } impl Sysroot { @@ -26,53 +37,73 @@ impl Sysroot { let sysroot_path = Path::new(stdout.trim()); let src = sysroot_path.join("lib/rustlib/src/rust/src"); - let crates: &[(&str, &[&str])] = &[ - ( - "std", - &[ - "alloc_jemalloc", - "alloc_system", - "panic_abort", - "rand", - "compiler_builtins", - "unwind", - "rustc_asan", - "rustc_lsan", - "rustc_msan", - "rustc_tsan", - "build_helper", - ], - ), - ("core", &[]), - ("alloc", &[]), - ("collections", &[]), - ("libc", &[]), - ("panic_unwind", &[]), - ("proc_macro", &[]), - ("rustc_unicode", &[]), - ("std_unicode", &[]), - ("test", &[]), - // Feature gated - ("alloc_jemalloc", &[]), - ("alloc_system", &[]), - ("compiler_builtins", &[]), - ("getopts", &[]), - ("panic_unwind", &[]), - ("panic_abort", &[]), - ("rand", &[]), - ("term", &[]), - ("unwind", &[]), - // Dependencies - ("build_helper", &[]), - ("rustc_asan", &[]), - ("rustc_lsan", &[]), - ("rustc_msan", &[]), - ("rustc_tsan", &[]), - ("syntax", &[]), - ]; + let mut sysroot = Sysroot { + crates: Arena::default(), + }; + for name in SYSROOT_CRATES.trim().lines() { + let path = src.join(format!("lib{}", name)).join("lib.rs"); + if path.exists() { + sysroot.crates.alloc(SysrootCrateData { + name: name.into(), + path, + deps: Vec::new(), + }); + } + } + if let Some(std) = sysroot.by_name("std") { + for dep in STD_DEPS.trim().lines() { + if let Some(dep) = sysroot.by_name(dep) { + sysroot.crates[std].deps.push(dep) + } + } + } + Ok(sysroot) + } - Ok(Sysroot { - crates: FxHashMap::default(), - }) + fn by_name(&self, name: &str) -> Option { + self.crates + .iter() + .find(|(_id, data)| data.name == name) + .map(|(id, _data)| id) } } + +const SYSROOT_CRATES: &str = " +std +core +alloc +collections +libc +panic_unwind +proc_macro +rustc_unicode +std_unicode +test +alloc_jemalloc +alloc_system +compiler_builtins +getopts +panic_unwind +panic_abort +rand +term +unwind +build_helper +rustc_asan +rustc_lsan +rustc_msan +rustc_tsan +syntax"; + +const STD_DEPS: &str = " +alloc_jemalloc +alloc_system +panic_abort +rand +compiler_builtins +unwind +rustc_asan +rustc_lsan +rustc_msan +rustc_tsan +build_helper"; -- cgit v1.2.3 From e35374ec7c26be8de61ec7c6175c2385ee5c006f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 10 Jan 2019 23:05:22 +0300 Subject: special case std --- crates/ra_lsp_server/src/project_model/sysroot.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/crates/ra_lsp_server/src/project_model/sysroot.rs b/crates/ra_lsp_server/src/project_model/sysroot.rs index 6c1a1a2a3..c4028a1fe 100644 --- a/crates/ra_lsp_server/src/project_model/sysroot.rs +++ b/crates/ra_lsp_server/src/project_model/sysroot.rs @@ -25,7 +25,11 @@ struct SysrootCrateData { } impl Sysroot { - pub(crate) fn discover(cargo_toml: &Path) -> Result { + pub(crate) fn std(&self) -> Option { + self.by_name("std") + } + + pub(super) fn discover(cargo_toml: &Path) -> Result { let rustc_output = Command::new("rustc") .current_dir(cargo_toml.parent().unwrap()) .args(&["--print", "sysroot"]) @@ -50,7 +54,7 @@ impl Sysroot { }); } } - if let Some(std) = sysroot.by_name("std") { + if let Some(std) = sysroot.std() { for dep in STD_DEPS.trim().lines() { if let Some(dep) = sysroot.by_name(dep) { sysroot.crates[std].deps.push(dep) -- cgit v1.2.3 From cd00158b1db9dd8565d2db08b4b0ebab9d5c00b3 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 11 Jan 2019 00:37:10 +0300 Subject: wire sysroot into crate graph --- crates/ra_lsp_server/src/project_model/sysroot.rs | 27 ++++++++++-- crates/ra_lsp_server/src/server_world.rs | 51 ++++++++++++++++++++++- crates/ra_lsp_server/tests/heavy_tests/main.rs | 41 +++++++----------- crates/ra_lsp_server/tests/heavy_tests/support.rs | 13 +++--- 4 files changed, 95 insertions(+), 37 deletions(-) diff --git a/crates/ra_lsp_server/src/project_model/sysroot.rs b/crates/ra_lsp_server/src/project_model/sysroot.rs index c4028a1fe..1dbab57f8 100644 --- a/crates/ra_lsp_server/src/project_model/sysroot.rs +++ b/crates/ra_lsp_server/src/project_model/sysroot.rs @@ -20,7 +20,7 @@ impl_arena_id!(SysrootCrate); #[derive(Debug, Clone)] struct SysrootCrateData { name: SmolStr, - path: PathBuf, + root: PathBuf, deps: Vec, } @@ -29,6 +29,10 @@ impl Sysroot { self.by_name("std") } + pub(crate) fn crates<'a>(&'a self) -> impl Iterator + 'a { + self.crates.iter().map(|(id, _data)| id) + } + pub(super) fn discover(cargo_toml: &Path) -> Result { let rustc_output = Command::new("rustc") .current_dir(cargo_toml.parent().unwrap()) @@ -45,11 +49,11 @@ impl Sysroot { crates: Arena::default(), }; for name in SYSROOT_CRATES.trim().lines() { - let path = src.join(format!("lib{}", name)).join("lib.rs"); - if path.exists() { + let root = src.join(format!("lib{}", name)).join("lib.rs"); + if root.exists() { sysroot.crates.alloc(SysrootCrateData { name: name.into(), - path, + root, deps: Vec::new(), }); } @@ -72,6 +76,21 @@ impl Sysroot { } } +impl SysrootCrate { + pub(crate) fn name(self, sysroot: &Sysroot) -> &SmolStr { + &sysroot.crates[self].name + } + pub(crate) fn root(self, sysroot: &Sysroot) -> &Path { + sysroot.crates[self].root.as_path() + } + pub(crate) fn root_dir(self, sysroot: &Sysroot) -> &Path { + self.root(sysroot).parent().unwrap() + } + pub(crate) fn deps<'a>(self, sysroot: &'a Sysroot) -> impl Iterator + 'a { + sysroot.crates[self].deps.iter().map(|&it| it) + } +} + const SYSROOT_CRATES: &str = " std core diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs index 2debbe557..4f3c231d3 100644 --- a/crates/ra_lsp_server/src/server_world.rs +++ b/crates/ra_lsp_server/src/server_world.rs @@ -44,7 +44,12 @@ impl ServerWorldState { for pkg in ws.cargo.packages() { roots.push(pkg.root(&ws.cargo).to_path_buf()); } + for krate in ws.sysroot.crates() { + roots.push(krate.root_dir(&ws.sysroot).to_path_buf()) + } } + roots.sort(); + roots.dedup(); let roots_to_scan = roots.len(); let (mut vfs, roots) = Vfs::new(roots); for r in roots { @@ -53,16 +58,43 @@ impl ServerWorldState { } let mut crate_graph = CrateGraph::default(); - let mut pkg_to_lib_crate = FxHashMap::default(); - let mut pkg_crates = FxHashMap::default(); for ws in workspaces.iter() { + // First, load std + let mut sysroot_crates = FxHashMap::default(); + for krate in ws.sysroot.crates() { + if let Some(file_id) = vfs.load(krate.root(&ws.sysroot)) { + let file_id = FileId(file_id.0.into()); + sysroot_crates.insert(krate, crate_graph.add_crate_root(file_id)); + } + } + for from in ws.sysroot.crates() { + for to in from.deps(&ws.sysroot) { + let name = to.name(&ws.sysroot); + if let (Some(&from), Some(&to)) = + (sysroot_crates.get(&from), sysroot_crates.get(&to)) + { + crate_graph.add_dep(from, name.clone(), to); + } + } + } + + let libstd = ws + .sysroot + .std() + .and_then(|it| sysroot_crates.get(&it).map(|&it| it)); + + let mut pkg_to_lib_crate = FxHashMap::default(); + let mut pkg_crates = FxHashMap::default(); + // Next, create crates for each package, target pair for pkg in ws.cargo.packages() { + let mut lib_tgt = None; for tgt in pkg.targets(&ws.cargo) { let root = tgt.root(&ws.cargo); if let Some(file_id) = vfs.load(root) { let file_id = FileId(file_id.0.into()); let crate_id = crate_graph.add_crate_root(file_id); if tgt.kind(&ws.cargo) == TargetKind::Lib { + lib_tgt = Some(crate_id); pkg_to_lib_crate.insert(pkg, crate_id); } pkg_crates @@ -71,7 +103,22 @@ impl ServerWorldState { .push(crate_id); } } + + // Set deps to the std and to the lib target of the current package + for &from in pkg_crates.get(&pkg).into_iter().flatten() { + if let Some(to) = lib_tgt { + if to != from { + crate_graph.add_dep(from, pkg.name(&ws.cargo).into(), to); + } + } + if let Some(std) = libstd { + crate_graph.add_dep(from, "std".into(), std); + } + } } + + // Now add a dep ednge from all targets of upstream to the lib + // target of downstream. for pkg in ws.cargo.packages() { for dep in pkg.dependencies(&ws.cargo) { if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) { diff --git a/crates/ra_lsp_server/tests/heavy_tests/main.rs b/crates/ra_lsp_server/tests/heavy_tests/main.rs index 927664ffb..02d62a259 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/main.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/main.rs @@ -1,5 +1,10 @@ mod support; +use std::{ + collections::HashMap, + time::Instant, +}; + use languageserver_types::{ CodeActionContext, DocumentFormattingParams, FormattingOptions, Position, Range, }; @@ -14,6 +19,7 @@ const LOG: &'static str = ""; #[test] fn completes_items_from_standard_library() { + let project_start = Instant::now(); let server = project( r#" //- Cargo.toml @@ -22,33 +28,19 @@ name = "foo" version = "0.0.0" //- src/lib.rs -use std::collections::; +use std::collections::Spam; "#, ); server.wait_for_feedback("workspace loaded"); - server.request::( - CompletionParams { - text_document: server.doc_id("src/lib.rs"), - context: None, - position: Position::new(0, 22), - }, - json!([ - { - "filterText": "self", - "insertText": "self", - "insertTextFormat": 1, - "kind": 14, - "label": "self" - }, - { - "filterText": "super", - "insertText": "super", - "insertTextFormat": 1, - "kind": 14, - "label": "super" - } - ]), - ); + eprintln!("loading took {:?}", project_start.elapsed()); + let completion_start = Instant::now(); + let res = server.send_request::(CompletionParams { + text_document: server.doc_id("src/lib.rs"), + context: None, + position: Position::new(0, 23), + }); + assert!(format!("{}", res).contains("HashMap")); + eprintln!("completion took {:?}", completion_start.elapsed()); } #[test] @@ -161,7 +153,6 @@ fn test_eggs() {} ); } -use std::collections::HashMap; #[test] fn test_format_document() { let server = project( diff --git a/crates/ra_lsp_server/tests/heavy_tests/support.rs b/crates/ra_lsp_server/tests/heavy_tests/support.rs index 7db168b0f..46107b6b6 100644 --- a/crates/ra_lsp_server/tests/heavy_tests/support.rs +++ b/crates/ra_lsp_server/tests/heavy_tests/support.rs @@ -93,9 +93,7 @@ impl Server { R: Request, R::Params: Serialize, { - let id = self.req_id.get(); - self.req_id.set(id + 1); - let actual = self.send_request::(id, params); + let actual = self.send_request::(params); match find_mismatch(&expected_resp, &actual) { Some((expected_part, actual_part)) => panic!( "JSON mismatch\nExpected:\n{}\nWas:\n{}\nExpected part:\n{}\nActual part:\n{}\n", @@ -108,11 +106,14 @@ impl Server { } } - fn send_request(&self, id: u64, params: R::Params) -> Value + pub fn send_request(&self, params: R::Params) -> Value where R: Request, R::Params: Serialize, { + let id = self.req_id.get(); + self.req_id.set(id + 1); + let r = RawRequest::new::(id, ¶ms); self.send_request_(r) } @@ -178,7 +179,7 @@ impl Server { impl Drop for Server { fn drop(&mut self) { - self.send_request::(666, ()); + self.send_request::(()); let receiver = self.worker.take().unwrap().shutdown(); while let Some(msg) = recv_timeout(&receiver) { drop(msg); @@ -188,7 +189,7 @@ impl Drop for Server { } fn recv_timeout(receiver: &Receiver) -> Option { - let timeout = Duration::from_secs(5); + let timeout = Duration::from_secs(50); select! { recv(receiver) -> msg => msg.ok(), recv(after(timeout)) -> _ => panic!("timed out"), -- cgit v1.2.3 From 4bf6b91b9dea117647a4120f404a2f0067041035 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 11 Jan 2019 00:40:35 +0300 Subject: minor --- crates/ra_lsp_server/src/project_model.rs | 4 +--- crates/ra_lsp_server/src/project_model/cargo_workspace.rs | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/crates/ra_lsp_server/src/project_model.rs b/crates/ra_lsp_server/src/project_model.rs index 3d02c6549..fd5875a0a 100644 --- a/crates/ra_lsp_server/src/project_model.rs +++ b/crates/ra_lsp_server/src/project_model.rs @@ -1,9 +1,7 @@ mod cargo_workspace; mod sysroot; -use std::{ - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; use failure::bail; use thread_worker::{WorkerHandle, Worker}; diff --git a/crates/ra_lsp_server/src/project_model/cargo_workspace.rs b/crates/ra_lsp_server/src/project_model/cargo_workspace.rs index 138fdee22..8f7518860 100644 --- a/crates/ra_lsp_server/src/project_model/cargo_workspace.rs +++ b/crates/ra_lsp_server/src/project_model/cargo_workspace.rs @@ -1,6 +1,4 @@ -use std::{ - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; use cargo_metadata::{metadata_run, CargoOpt}; use ra_syntax::SmolStr; -- cgit v1.2.3 From 6b7699d187d51e8a26ba1a18a217a77dd236458f Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Fri, 11 Jan 2019 00:48:36 +0300 Subject: install rust-src component on CI --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 789adcbe3..12e4da4a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,7 @@ build: &rust_build rust: 1.31.1 script: - rustup component add rustfmt + - rustup component add rust-src - cargo gen-tests --verify - cargo gen-syntax --verify - cargo test --no-run # let's measure compile time separately -- cgit v1.2.3