diff options
author | Aleksey Kladov <[email protected]> | 2020-04-16 21:19:38 +0100 |
---|---|---|
committer | Aleksey Kladov <[email protected]> | 2020-04-16 21:35:50 +0100 |
commit | 422ae477ce2a52c8d004ce629318f0b7c1d89638 (patch) | |
tree | 50b9ce2048599e53d68936ba317aa28c6231b4b1 | |
parent | be2654b0ed3e4eb51bc3745b2329d6264588549f (diff) |
Unmix error handling when discovering workspaces
Hitting an io::Error is a legit problem. Finding more than one
Cargo.toml is not.
-rw-r--r-- | crates/ra_project_model/src/lib.rs | 78 | ||||
-rw-r--r-- | crates/rust-analyzer/src/cli/load_cargo.rs | 2 | ||||
-rw-r--r-- | crates/rust-analyzer/src/main_loop.rs | 36 |
3 files changed, 41 insertions, 75 deletions
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index df4be94dc..03f2629da 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -5,9 +5,8 @@ mod json_project; | |||
5 | mod sysroot; | 5 | mod sysroot; |
6 | 6 | ||
7 | use std::{ | 7 | use std::{ |
8 | error::Error, | ||
9 | fs::{read_dir, File, ReadDir}, | 8 | fs::{read_dir, File, ReadDir}, |
10 | io::BufReader, | 9 | io::{self, BufReader}, |
11 | path::{Path, PathBuf}, | 10 | path::{Path, PathBuf}, |
12 | process::Command, | 11 | process::Command, |
13 | }; | 12 | }; |
@@ -25,25 +24,6 @@ pub use crate::{ | |||
25 | }; | 24 | }; |
26 | pub use ra_proc_macro::ProcMacroClient; | 25 | pub use ra_proc_macro::ProcMacroClient; |
27 | 26 | ||
28 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] | ||
29 | pub struct CargoTomlNotFoundError { | ||
30 | pub searched_at: PathBuf, | ||
31 | pub reason: String, | ||
32 | } | ||
33 | |||
34 | impl std::fmt::Display for CargoTomlNotFoundError { | ||
35 | fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
36 | write!( | ||
37 | fmt, | ||
38 | "can't find Cargo.toml at {}, due to {}", | ||
39 | self.searched_at.display(), | ||
40 | self.reason | ||
41 | ) | ||
42 | } | ||
43 | } | ||
44 | |||
45 | impl Error for CargoTomlNotFoundError {} | ||
46 | |||
47 | #[derive(Debug, Clone)] | 27 | #[derive(Debug, Clone)] |
48 | pub enum ProjectWorkspace { | 28 | pub enum ProjectWorkspace { |
49 | /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. | 29 | /// Project workspace was discovered by running `cargo metadata` and `rustc --print sysroot`. |
@@ -94,11 +74,25 @@ impl ProjectRoot { | |||
94 | bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) | 74 | bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display()) |
95 | } | 75 | } |
96 | 76 | ||
97 | pub fn discover(path: &Path) -> Result<ProjectRoot, CargoTomlNotFoundError> { | 77 | pub fn discover_single(path: &Path) -> Result<ProjectRoot> { |
78 | let mut candidates = ProjectRoot::discover(path)?; | ||
79 | let res = match candidates.pop() { | ||
80 | None => bail!("no projects"), | ||
81 | Some(it) => it, | ||
82 | }; | ||
83 | |||
84 | if !candidates.is_empty() { | ||
85 | bail!("more than one project") | ||
86 | } | ||
87 | Ok(res) | ||
88 | } | ||
89 | |||
90 | pub fn discover(path: &Path) -> io::Result<Vec<ProjectRoot>> { | ||
98 | if let Some(project_json) = find_rust_project_json(path) { | 91 | if let Some(project_json) = find_rust_project_json(path) { |
99 | return Ok(ProjectRoot::ProjectJson(project_json)); | 92 | return Ok(vec![ProjectRoot::ProjectJson(project_json)]); |
100 | } | 93 | } |
101 | return find_cargo_toml(path).map(ProjectRoot::CargoToml); | 94 | return find_cargo_toml(path) |
95 | .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect()); | ||
102 | 96 | ||
103 | fn find_rust_project_json(path: &Path) -> Option<PathBuf> { | 97 | fn find_rust_project_json(path: &Path) -> Option<PathBuf> { |
104 | if path.ends_with("rust-project.json") { | 98 | if path.ends_with("rust-project.json") { |
@@ -117,43 +111,17 @@ impl ProjectRoot { | |||
117 | None | 111 | None |
118 | } | 112 | } |
119 | 113 | ||
120 | fn find_cargo_toml(path: &Path) -> Result<PathBuf, CargoTomlNotFoundError> { | 114 | fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> { |
121 | if path.ends_with("Cargo.toml") { | 115 | if path.ends_with("Cargo.toml") { |
122 | return Ok(path.to_path_buf()); | 116 | return Ok(vec![path.to_path_buf()]); |
123 | } | 117 | } |
124 | 118 | ||
125 | if let Some(p) = find_cargo_toml_in_parent_dir(path) { | 119 | if let Some(p) = find_cargo_toml_in_parent_dir(path) { |
126 | return Ok(p); | 120 | return Ok(vec![p]); |
127 | } | 121 | } |
128 | 122 | ||
129 | let entities = match read_dir(path) { | 123 | let entities = read_dir(path)?; |
130 | Ok(entities) => entities, | 124 | Ok(find_cargo_toml_in_child_dir(entities)) |
131 | Err(e) => { | ||
132 | return Err(CargoTomlNotFoundError { | ||
133 | searched_at: path.to_path_buf(), | ||
134 | reason: format!("file system error: {}", e), | ||
135 | } | ||
136 | .into()); | ||
137 | } | ||
138 | }; | ||
139 | |||
140 | let mut valid_canditates = find_cargo_toml_in_child_dir(entities); | ||
141 | return match valid_canditates.len() { | ||
142 | 1 => Ok(valid_canditates.remove(0)), | ||
143 | 0 => Err(CargoTomlNotFoundError { | ||
144 | searched_at: path.to_path_buf(), | ||
145 | reason: "no Cargo.toml file found".to_string(), | ||
146 | } | ||
147 | .into()), | ||
148 | _ => Err(CargoTomlNotFoundError { | ||
149 | searched_at: path.to_path_buf(), | ||
150 | reason: format!( | ||
151 | "multiple equally valid Cargo.toml files found: {:?}", | ||
152 | valid_canditates | ||
153 | ), | ||
154 | } | ||
155 | .into()), | ||
156 | }; | ||
157 | } | 125 | } |
158 | 126 | ||
159 | fn find_cargo_toml_in_parent_dir(path: &Path) -> Option<PathBuf> { | 127 | fn find_cargo_toml_in_parent_dir(path: &Path) -> Option<PathBuf> { |
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index f3f266ee4..e620712ad 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs | |||
@@ -27,7 +27,7 @@ pub(crate) fn load_cargo( | |||
27 | load_out_dirs_from_check: bool, | 27 | load_out_dirs_from_check: bool, |
28 | ) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> { | 28 | ) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> { |
29 | let root = std::env::current_dir()?.join(root); | 29 | let root = std::env::current_dir()?.join(root); |
30 | let root = ProjectRoot::discover(&root)?; | 30 | let root = ProjectRoot::discover_single(&root)?; |
31 | let ws = ProjectWorkspace::load( | 31 | let ws = ProjectWorkspace::load( |
32 | root, | 32 | root, |
33 | &CargoConfig { load_out_dirs_from_check, ..Default::default() }, | 33 | &CargoConfig { load_out_dirs_from_check, ..Default::default() }, |
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 9c345601c..fc4c77f8a 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs | |||
@@ -15,6 +15,7 @@ use std::{ | |||
15 | }; | 15 | }; |
16 | 16 | ||
17 | use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; | 17 | use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; |
18 | use itertools::Itertools; | ||
18 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; | 19 | use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; |
19 | use lsp_types::{ | 20 | use lsp_types::{ |
20 | NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams, | 21 | NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams, |
@@ -93,27 +94,24 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection) | |||
93 | let mut visited = FxHashSet::default(); | 94 | let mut visited = FxHashSet::default(); |
94 | let project_roots = ws_roots | 95 | let project_roots = ws_roots |
95 | .iter() | 96 | .iter() |
96 | .map(|it| ra_project_model::ProjectRoot::discover(it)) | 97 | .filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok()) |
97 | .filter_map(|dir| { | 98 | .flatten() |
98 | dir.map_err(|cargo_toml_not_found| { | 99 | .filter(|it| visited.insert(it.clone())) |
99 | log::error!("discovering workspace failed: {:?}", cargo_toml_not_found); | 100 | .collect::<Vec<_>>(); |
100 | 101 | ||
101 | if config.notifications.cargo_toml_not_found { | 102 | if project_roots.is_empty() && config.notifications.cargo_toml_not_found { |
102 | show_message( | 103 | show_message( |
103 | req::MessageType::Error, | 104 | req::MessageType::Error, |
104 | format!( | 105 | format!( |
105 | "rust-analyzer failed to discover workspace: {:?}", | 106 | "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}", |
106 | cargo_toml_not_found | 107 | ws_roots.iter().format_with(", ", |it, f| f(&it.display())) |
107 | ), | 108 | ), |
108 | &connection.sender, | 109 | &connection.sender, |
109 | ); | 110 | ); |
110 | } | 111 | }; |
111 | }) | ||
112 | .ok() | ||
113 | }) | ||
114 | .filter(|it| visited.insert(it.clone())); | ||
115 | 112 | ||
116 | project_roots | 113 | project_roots |
114 | .into_iter() | ||
117 | .filter_map(|root| { | 115 | .filter_map(|root| { |
118 | ra_project_model::ProjectWorkspace::load( | 116 | ra_project_model::ProjectWorkspace::load( |
119 | root, | 117 | root, |