aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_lsp_server/src
diff options
context:
space:
mode:
authorbors[bot] <bors[bot]@users.noreply.github.com>2019-02-09 10:21:13 +0000
committerbors[bot] <bors[bot]@users.noreply.github.com>2019-02-09 10:21:13 +0000
commitd0a32627a741826502692f2c3de71512b7ec23cf (patch)
treeee3cef56bd9738f5c3b98caae9b8ce1b8dbf401f /crates/ra_lsp_server/src
parent34398a8756b56c323d3b4b2ef32fbca32d88a105 (diff)
parente91a46eb0c4a355af25656d77dead55c2e29258e (diff)
Merge #767
767: Extract project model to separate crate r=matklad a=flodiebold I'm looking into creating a separate crate that would allow getting a HIR db for a project for 'batch' analyses, and this seems to be an obvious first step. We'd probably want to change the error handling to not rely on failure, though, right? Co-authored-by: Florian Diebold <[email protected]>
Diffstat (limited to 'crates/ra_lsp_server/src')
-rw-r--r--crates/ra_lsp_server/src/project_model.rs42
-rw-r--r--crates/ra_lsp_server/src/project_model/cargo_workspace.rs171
-rw-r--r--crates/ra_lsp_server/src/project_model/sysroot.rs137
-rw-r--r--crates/ra_lsp_server/src/server_world.rs89
4 files changed, 10 insertions, 429 deletions
diff --git a/crates/ra_lsp_server/src/project_model.rs b/crates/ra_lsp_server/src/project_model.rs
index fd5875a0a..6800eb138 100644
--- a/crates/ra_lsp_server/src/project_model.rs
+++ b/crates/ra_lsp_server/src/project_model.rs
@@ -1,34 +1,13 @@
1mod cargo_workspace; 1use std::path::PathBuf;
2mod sysroot;
3 2
4use std::path::{Path, PathBuf};
5
6use failure::bail;
7use thread_worker::{WorkerHandle, Worker}; 3use thread_worker::{WorkerHandle, Worker};
8 4
9use crate::Result; 5use crate::Result;
10 6
11pub use crate::project_model::{ 7pub use ra_project_model::{
12 cargo_workspace::{CargoWorkspace, Package, Target, TargetKind}, 8 ProjectWorkspace, CargoWorkspace, Package, Target, TargetKind, Sysroot,
13 sysroot::Sysroot,
14}; 9};
15 10
16#[derive(Debug, Clone)]
17pub struct ProjectWorkspace {
18 pub(crate) cargo: CargoWorkspace,
19 pub(crate) sysroot: Sysroot,
20}
21
22impl ProjectWorkspace {
23 pub fn discover(path: &Path) -> Result<ProjectWorkspace> {
24 let cargo_toml = find_cargo_toml(path)?;
25 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml)?;
26 let sysroot = Sysroot::discover(&cargo_toml)?;
27 let res = ProjectWorkspace { cargo, sysroot };
28 Ok(res)
29 }
30}
31
32pub fn workspace_loader() -> (Worker<PathBuf, Result<ProjectWorkspace>>, WorkerHandle) { 11pub fn workspace_loader() -> (Worker<PathBuf, Result<ProjectWorkspace>>, WorkerHandle) {
33 thread_worker::spawn::<PathBuf, Result<ProjectWorkspace>, _>( 12 thread_worker::spawn::<PathBuf, Result<ProjectWorkspace>, _>(
34 "workspace loader", 13 "workspace loader",
@@ -42,18 +21,3 @@ pub fn workspace_loader() -> (Worker<PathBuf, Result<ProjectWorkspace>>, WorkerH
42 }, 21 },
43 ) 22 )
44} 23}
45
46fn find_cargo_toml(path: &Path) -> Result<PathBuf> {
47 if path.ends_with("Cargo.toml") {
48 return Ok(path.to_path_buf());
49 }
50 let mut curr = Some(path);
51 while let Some(path) = curr {
52 let candidate = path.join("Cargo.toml");
53 if candidate.exists() {
54 return Ok(candidate);
55 }
56 curr = path.parent();
57 }
58 bail!("can't find Cargo.toml at {}", path.display())
59}
diff --git a/crates/ra_lsp_server/src/project_model/cargo_workspace.rs b/crates/ra_lsp_server/src/project_model/cargo_workspace.rs
deleted file mode 100644
index 3b76389d2..000000000
--- a/crates/ra_lsp_server/src/project_model/cargo_workspace.rs
+++ /dev/null
@@ -1,171 +0,0 @@
1use std::path::{Path, PathBuf};
2
3use cargo_metadata::{MetadataCommand, CargoOpt};
4use ra_syntax::SmolStr;
5use ra_arena::{Arena, RawId, impl_arena_id};
6use rustc_hash::FxHashMap;
7use failure::format_err;
8
9use crate::Result;
10
11/// `CargoWorksapce` represents the logical structure of, well, a Cargo
12/// workspace. It pretty closely mirrors `cargo metadata` output.
13///
14/// Note that internally, rust analyzer uses a differnet structure:
15/// `CrateGraph`. `CrateGraph` is lower-level: it knows only about the crates,
16/// while this knows about `Pacakges` & `Targets`: purely cargo-related
17/// concepts.
18#[derive(Debug, Clone)]
19pub struct CargoWorkspace {
20 packages: Arena<Package, PackageData>,
21 targets: Arena<Target, TargetData>,
22}
23
24#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
25pub struct Package(RawId);
26impl_arena_id!(Package);
27
28#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
29pub struct Target(RawId);
30impl_arena_id!(Target);
31
32#[derive(Debug, Clone)]
33struct PackageData {
34 name: SmolStr,
35 manifest: PathBuf,
36 targets: Vec<Target>,
37 is_member: bool,
38 dependencies: Vec<PackageDependency>,
39}
40
41#[derive(Debug, Clone)]
42pub struct PackageDependency {
43 pub pkg: Package,
44 pub name: SmolStr,
45}
46
47#[derive(Debug, Clone)]
48struct TargetData {
49 pkg: Package,
50 name: SmolStr,
51 root: PathBuf,
52 kind: TargetKind,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56pub enum TargetKind {
57 Bin,
58 Lib,
59 Example,
60 Test,
61 Bench,
62 Other,
63}
64
65impl TargetKind {
66 fn new(kinds: &[String]) -> TargetKind {
67 for kind in kinds {
68 return match kind.as_str() {
69 "bin" => TargetKind::Bin,
70 "test" => TargetKind::Test,
71 "bench" => TargetKind::Bench,
72 "example" => TargetKind::Example,
73 _ if kind.contains("lib") => TargetKind::Lib,
74 _ => continue,
75 };
76 }
77 TargetKind::Other
78 }
79}
80
81impl Package {
82 pub fn name(self, ws: &CargoWorkspace) -> &str {
83 ws.packages[self].name.as_str()
84 }
85 pub fn root(self, ws: &CargoWorkspace) -> &Path {
86 ws.packages[self].manifest.parent().unwrap()
87 }
88 pub fn targets<'a>(self, ws: &'a CargoWorkspace) -> impl Iterator<Item = Target> + 'a {
89 ws.packages[self].targets.iter().cloned()
90 }
91 #[allow(unused)]
92 pub fn is_member(self, ws: &CargoWorkspace) -> bool {
93 ws.packages[self].is_member
94 }
95 pub fn dependencies<'a>(
96 self,
97 ws: &'a CargoWorkspace,
98 ) -> impl Iterator<Item = &'a PackageDependency> + 'a {
99 ws.packages[self].dependencies.iter()
100 }
101}
102
103impl Target {
104 pub fn package(self, ws: &CargoWorkspace) -> Package {
105 ws.targets[self].pkg
106 }
107 pub fn name(self, ws: &CargoWorkspace) -> &str {
108 ws.targets[self].name.as_str()
109 }
110 pub fn root(self, ws: &CargoWorkspace) -> &Path {
111 ws.targets[self].root.as_path()
112 }
113 pub fn kind(self, ws: &CargoWorkspace) -> TargetKind {
114 ws.targets[self].kind
115 }
116}
117
118impl CargoWorkspace {
119 pub fn from_cargo_metadata(cargo_toml: &Path) -> Result<CargoWorkspace> {
120 let mut meta = MetadataCommand::new();
121 meta.manifest_path(cargo_toml).features(CargoOpt::AllFeatures);
122 if let Some(parent) = cargo_toml.parent() {
123 meta.current_dir(parent);
124 }
125 let meta = meta.exec().map_err(|e| format_err!("cargo metadata failed: {}", e))?;
126 let mut pkg_by_id = FxHashMap::default();
127 let mut packages = Arena::default();
128 let mut targets = Arena::default();
129
130 let ws_members = &meta.workspace_members;
131
132 for meta_pkg in meta.packages {
133 let is_member = ws_members.contains(&meta_pkg.id);
134 let pkg = packages.alloc(PackageData {
135 name: meta_pkg.name.into(),
136 manifest: meta_pkg.manifest_path.clone(),
137 targets: Vec::new(),
138 is_member,
139 dependencies: Vec::new(),
140 });
141 let pkg_data = &mut packages[pkg];
142 pkg_by_id.insert(meta_pkg.id.clone(), pkg);
143 for meta_tgt in meta_pkg.targets {
144 let tgt = targets.alloc(TargetData {
145 pkg,
146 name: meta_tgt.name.into(),
147 root: meta_tgt.src_path.clone(),
148 kind: TargetKind::new(meta_tgt.kind.as_slice()),
149 });
150 pkg_data.targets.push(tgt);
151 }
152 }
153 let resolve = meta.resolve.expect("metadata executed with deps");
154 for node in resolve.nodes {
155 let source = pkg_by_id[&node.id];
156 for dep_node in node.deps {
157 let dep =
158 PackageDependency { name: dep_node.name.into(), pkg: pkg_by_id[&dep_node.pkg] };
159 packages[source].dependencies.push(dep);
160 }
161 }
162
163 Ok(CargoWorkspace { packages, targets })
164 }
165 pub fn packages<'a>(&'a self) -> impl Iterator<Item = Package> + 'a {
166 self.packages.iter().map(|(id, _pkg)| id)
167 }
168 pub fn target_by_root(&self, root: &Path) -> Option<Target> {
169 self.packages().filter_map(|pkg| pkg.targets(self).find(|it| it.root(self) == root)).next()
170 }
171}
diff --git a/crates/ra_lsp_server/src/project_model/sysroot.rs b/crates/ra_lsp_server/src/project_model/sysroot.rs
deleted file mode 100644
index 49210ac7a..000000000
--- a/crates/ra_lsp_server/src/project_model/sysroot.rs
+++ /dev/null
@@ -1,137 +0,0 @@
1use std::{
2 path::{Path, PathBuf},
3 process::Command,
4};
5
6use ra_syntax::SmolStr;
7use ra_arena::{Arena, RawId, impl_arena_id};
8
9use crate::Result;
10
11#[derive(Debug, Clone)]
12pub struct Sysroot {
13 crates: Arena<SysrootCrate, SysrootCrateData>,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub struct SysrootCrate(RawId);
18impl_arena_id!(SysrootCrate);
19
20#[derive(Debug, Clone)]
21struct SysrootCrateData {
22 name: SmolStr,
23 root: PathBuf,
24 deps: Vec<SysrootCrate>,
25}
26
27impl Sysroot {
28 pub(crate) fn std(&self) -> Option<SysrootCrate> {
29 self.by_name("std")
30 }
31
32 pub(crate) fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + 'a {
33 self.crates.iter().map(|(id, _data)| id)
34 }
35
36 pub(super) fn discover(cargo_toml: &Path) -> Result<Sysroot> {
37 let rustc_output = Command::new("rustc")
38 .current_dir(cargo_toml.parent().unwrap())
39 .args(&["--print", "sysroot"])
40 .output()?;
41 if !rustc_output.status.success() {
42 failure::bail!("failed to locate sysroot")
43 }
44 let stdout = String::from_utf8(rustc_output.stdout)?;
45 let sysroot_path = Path::new(stdout.trim());
46 let src = sysroot_path.join("lib/rustlib/src/rust/src");
47 if !src.exists() {
48 failure::bail!(
49 "can't load standard library from sysroot\n\
50 {:?}\n\
51 try running `rustup component add rust-src`",
52 src,
53 );
54 }
55
56 let mut sysroot = Sysroot { crates: Arena::default() };
57 for name in SYSROOT_CRATES.trim().lines() {
58 let root = src.join(format!("lib{}", name)).join("lib.rs");
59 if root.exists() {
60 sysroot.crates.alloc(SysrootCrateData {
61 name: name.into(),
62 root,
63 deps: Vec::new(),
64 });
65 }
66 }
67 if let Some(std) = sysroot.std() {
68 for dep in STD_DEPS.trim().lines() {
69 if let Some(dep) = sysroot.by_name(dep) {
70 sysroot.crates[std].deps.push(dep)
71 }
72 }
73 }
74 Ok(sysroot)
75 }
76
77 fn by_name(&self, name: &str) -> Option<SysrootCrate> {
78 self.crates.iter().find(|(_id, data)| data.name == name).map(|(id, _data)| id)
79 }
80}
81
82impl SysrootCrate {
83 pub(crate) fn name(self, sysroot: &Sysroot) -> &SmolStr {
84 &sysroot.crates[self].name
85 }
86 pub(crate) fn root(self, sysroot: &Sysroot) -> &Path {
87 sysroot.crates[self].root.as_path()
88 }
89 pub(crate) fn root_dir(self, sysroot: &Sysroot) -> &Path {
90 self.root(sysroot).parent().unwrap()
91 }
92 pub(crate) fn deps<'a>(self, sysroot: &'a Sysroot) -> impl Iterator<Item = SysrootCrate> + 'a {
93 sysroot.crates[self].deps.iter().map(|&it| it)
94 }
95}
96
97const SYSROOT_CRATES: &str = "
98std
99core
100alloc
101collections
102libc
103panic_unwind
104proc_macro
105rustc_unicode
106std_unicode
107test
108alloc_jemalloc
109alloc_system
110compiler_builtins
111getopts
112panic_unwind
113panic_abort
114rand
115term
116unwind
117build_helper
118rustc_asan
119rustc_lsan
120rustc_msan
121rustc_tsan
122syntax";
123
124const STD_DEPS: &str = "
125alloc
126alloc_jemalloc
127alloc_system
128core
129panic_abort
130rand
131compiler_builtins
132unwind
133rustc_asan
134rustc_lsan
135rustc_msan
136rustc_tsan
137build_helper";
diff --git a/crates/ra_lsp_server/src/server_world.rs b/crates/ra_lsp_server/src/server_world.rs
index 02f2a37a8..f97d240fa 100644
--- a/crates/ra_lsp_server/src/server_world.rs
+++ b/crates/ra_lsp_server/src/server_world.rs
@@ -9,13 +9,12 @@ use ra_ide_api::{
9 SourceRootId 9 SourceRootId
10}; 10};
11use ra_vfs::{Vfs, VfsChange, VfsFile, VfsRoot}; 11use ra_vfs::{Vfs, VfsChange, VfsFile, VfsRoot};
12use rustc_hash::FxHashMap;
13use relative_path::RelativePathBuf; 12use relative_path::RelativePathBuf;
14use parking_lot::RwLock; 13use parking_lot::RwLock;
15use failure::format_err; 14use failure::format_err;
16 15
17use crate::{ 16use crate::{
18 project_model::{ProjectWorkspace, TargetKind}, 17 project_model::ProjectWorkspace,
19 Result, 18 Result,
20}; 19};
21 20
@@ -57,88 +56,14 @@ impl ServerWorldState {
57 change.add_root(SourceRootId(r.0.into()), is_local); 56 change.add_root(SourceRootId(r.0.into()), is_local);
58 } 57 }
59 58
59 // Create crate graph from all the workspaces
60 let mut crate_graph = CrateGraph::default(); 60 let mut crate_graph = CrateGraph::default();
61 let mut load = |path: &std::path::Path| {
62 let vfs_file = vfs.load(path);
63 vfs_file.map(|f| FileId(f.0.into()))
64 };
61 for ws in workspaces.iter() { 65 for ws in workspaces.iter() {
62 // First, load std 66 crate_graph.extend(ws.to_crate_graph(&mut load));
63 let mut sysroot_crates = FxHashMap::default();
64 for krate in ws.sysroot.crates() {
65 if let Some(file_id) = vfs.load(krate.root(&ws.sysroot)) {
66 let file_id = FileId(file_id.0.into());
67 sysroot_crates.insert(krate, crate_graph.add_crate_root(file_id));
68 }
69 }
70 for from in ws.sysroot.crates() {
71 for to in from.deps(&ws.sysroot) {
72 let name = to.name(&ws.sysroot);
73 if let (Some(&from), Some(&to)) =
74 (sysroot_crates.get(&from), sysroot_crates.get(&to))
75 {
76 if let Err(_) = crate_graph.add_dep(from, name.clone(), to) {
77 log::error!("cyclic dependency between sysroot crates")
78 }
79 }
80 }
81 }
82
83 let libstd = ws.sysroot.std().and_then(|it| sysroot_crates.get(&it).map(|&it| it));
84
85 let mut pkg_to_lib_crate = FxHashMap::default();
86 let mut pkg_crates = FxHashMap::default();
87 // Next, create crates for each package, target pair
88 for pkg in ws.cargo.packages() {
89 let mut lib_tgt = None;
90 for tgt in pkg.targets(&ws.cargo) {
91 let root = tgt.root(&ws.cargo);
92 if let Some(file_id) = vfs.load(root) {
93 let file_id = FileId(file_id.0.into());
94 let crate_id = crate_graph.add_crate_root(file_id);
95 if tgt.kind(&ws.cargo) == TargetKind::Lib {
96 lib_tgt = Some(crate_id);
97 pkg_to_lib_crate.insert(pkg, crate_id);
98 }
99 pkg_crates.entry(pkg).or_insert_with(Vec::new).push(crate_id);
100 }
101 }
102
103 // Set deps to the std and to the lib target of the current package
104 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
105 if let Some(to) = lib_tgt {
106 if to != from {
107 if let Err(_) =
108 crate_graph.add_dep(from, pkg.name(&ws.cargo).into(), to)
109 {
110 log::error!(
111 "cyclic dependency between targets of {}",
112 pkg.name(&ws.cargo)
113 )
114 }
115 }
116 }
117 if let Some(std) = libstd {
118 if let Err(_) = crate_graph.add_dep(from, "std".into(), std) {
119 log::error!("cyclic dependency on std for {}", pkg.name(&ws.cargo))
120 }
121 }
122 }
123 }
124
125 // Now add a dep ednge from all targets of upstream to the lib
126 // target of downstream.
127 for pkg in ws.cargo.packages() {
128 for dep in pkg.dependencies(&ws.cargo) {
129 if let Some(&to) = pkg_to_lib_crate.get(&dep.pkg) {
130 for &from in pkg_crates.get(&pkg).into_iter().flatten() {
131 if let Err(_) = crate_graph.add_dep(from, dep.name.clone(), to) {
132 log::error!(
133 "cyclic dependency {} -> {}",
134 pkg.name(&ws.cargo),
135 dep.pkg.name(&ws.cargo)
136 )
137 }
138 }
139 }
140 }
141 }
142 } 67 }
143 change.set_crate_graph(crate_graph); 68 change.set_crate_graph(crate_graph);
144 69