aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_proc_macro_srv/src/dylib.rs72
-rw-r--r--crates/ra_project_model/src/lib.rs231
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs8
-rw-r--r--crates/rust-analyzer/src/main_loop.rs60
-rw-r--r--xtask/tests/tidy-tests/main.rs6
5 files changed, 188 insertions, 189 deletions
diff --git a/crates/ra_proc_macro_srv/src/dylib.rs b/crates/ra_proc_macro_srv/src/dylib.rs
index ec63d587b..7d6e5d323 100644
--- a/crates/ra_proc_macro_srv/src/dylib.rs
+++ b/crates/ra_proc_macro_srv/src/dylib.rs
@@ -16,55 +16,53 @@ fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> I
16 IoError::new(IoErrorKind::InvalidData, e) 16 IoError::new(IoErrorKind::InvalidData, e)
17} 17}
18 18
19fn get_symbols_from_lib(file: &Path) -> Result<Vec<String>, IoError> { 19fn is_derive_registrar_symbol(symbol: &str) -> bool {
20 symbol.contains(NEW_REGISTRAR_SYMBOL)
21}
22
23fn find_registrar_symbol(file: &Path) -> Result<Option<String>, IoError> {
20 let buffer = std::fs::read(file)?; 24 let buffer = std::fs::read(file)?;
21 let object = Object::parse(&buffer).map_err(invalid_data_err)?; 25 let object = Object::parse(&buffer).map_err(invalid_data_err)?;
22 26
23 match object { 27 match object {
24 Object::Elf(elf) => { 28 Object::Elf(elf) => {
25 let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?; 29 let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?;
26 let names = symbols.iter().map(|s| s.to_string()).collect(); 30 let name =
27 Ok(names) 31 symbols.iter().find(|s| is_derive_registrar_symbol(s)).map(|s| s.to_string());
32 Ok(name)
28 } 33 }
29 Object::PE(pe) => { 34 Object::PE(pe) => {
30 let symbol_names = 35 let name = pe
31 pe.exports.iter().flat_map(|s| s.name).map(|n| n.to_string()).collect(); 36 .exports
32 Ok(symbol_names) 37 .iter()
38 .flat_map(|s| s.name)
39 .find(|s| is_derive_registrar_symbol(s))
40 .map(|s| s.to_string());
41 Ok(name)
33 } 42 }
34 Object::Mach(mach) => match mach { 43 Object::Mach(Mach::Binary(binary)) => {
35 Mach::Binary(binary) => { 44 let exports = binary.exports().map_err(invalid_data_err)?;
36 let exports = binary.exports().map_err(invalid_data_err)?; 45 let name = exports
37 let names = exports 46 .iter()
38 .into_iter() 47 .map(|s| {
39 .map(|s| { 48 // In macos doc:
40 // In macos doc: 49 // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
41 // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html 50 // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
42 // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be 51 // prepended with an underscore.
43 // prepended with an underscore. 52 if s.name.starts_with("_") {
44 if s.name.starts_with("_") { 53 &s.name[1..]
45 s.name[1..].to_string() 54 } else {
46 } else { 55 &s.name
47 s.name 56 }
48 } 57 })
49 }) 58 .find(|s| is_derive_registrar_symbol(&s))
50 .collect(); 59 .map(|s| s.to_string());
51 Ok(names) 60 Ok(name)
52 } 61 }
53 Mach::Fat(_) => Ok(vec![]), 62 _ => Ok(None),
54 },
55 Object::Archive(_) | Object::Unknown(_) => Ok(vec![]),
56 } 63 }
57} 64}
58 65
59fn is_derive_registrar_symbol(symbol: &str) -> bool {
60 symbol.contains(NEW_REGISTRAR_SYMBOL)
61}
62
63fn find_registrar_symbol(file: &Path) -> Result<Option<String>, IoError> {
64 let symbols = get_symbols_from_lib(file)?;
65 Ok(symbols.into_iter().find(|s| is_derive_registrar_symbol(s)))
66}
67
68/// Loads dynamic library in platform dependent manner. 66/// Loads dynamic library in platform dependent manner.
69/// 67///
70/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described 68/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs
index 0ab64a1e0..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;
5mod sysroot; 5mod sysroot;
6 6
7use std::{ 7use 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};
26pub use ra_proc_macro::ProcMacroClient; 25pub use ra_proc_macro::ProcMacroClient;
27 26
28#[derive(Clone, PartialEq, Eq, Hash, Debug)]
29pub struct CargoTomlNotFoundError {
30 pub searched_at: PathBuf,
31 pub reason: String,
32}
33
34impl 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
45impl Error for CargoTomlNotFoundError {}
46
47#[derive(Debug, Clone)] 27#[derive(Debug, Clone)]
48pub enum ProjectWorkspace { 28pub 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`.
@@ -77,31 +57,119 @@ impl PackageRoot {
77 } 57 }
78} 58}
79 59
80impl ProjectWorkspace { 60#[derive(Debug, Clone, PartialEq, Eq, Hash)]
81 pub fn discover(path: &Path, cargo_features: &CargoConfig) -> Result<ProjectWorkspace> { 61pub enum ProjectRoot {
82 ProjectWorkspace::discover_with_sysroot(path, true, cargo_features) 62 ProjectJson(PathBuf),
63 CargoToml(PathBuf),
64}
65
66impl ProjectRoot {
67 pub fn from_manifest_file(path: PathBuf) -> Result<ProjectRoot> {
68 if path.ends_with("rust-project.json") {
69 return Ok(ProjectRoot::ProjectJson(path));
70 }
71 if path.ends_with("Cargo.toml") {
72 return Ok(ProjectRoot::CargoToml(path));
73 }
74 bail!("project root must point to Cargo.toml or rust-project.json: {}", path.display())
83 } 75 }
84 76
85 pub fn discover_with_sysroot( 77 pub fn discover_single(path: &Path) -> Result<ProjectRoot> {
86 path: &Path, 78 let mut candidates = ProjectRoot::discover(path)?;
87 with_sysroot: bool, 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>> {
91 if let Some(project_json) = find_rust_project_json(path) {
92 return Ok(vec![ProjectRoot::ProjectJson(project_json)]);
93 }
94 return find_cargo_toml(path)
95 .map(|paths| paths.into_iter().map(ProjectRoot::CargoToml).collect());
96
97 fn find_rust_project_json(path: &Path) -> Option<PathBuf> {
98 if path.ends_with("rust-project.json") {
99 return Some(path.to_path_buf());
100 }
101
102 let mut curr = Some(path);
103 while let Some(path) = curr {
104 let candidate = path.join("rust-project.json");
105 if candidate.exists() {
106 return Some(candidate);
107 }
108 curr = path.parent();
109 }
110
111 None
112 }
113
114 fn find_cargo_toml(path: &Path) -> io::Result<Vec<PathBuf>> {
115 if path.ends_with("Cargo.toml") {
116 return Ok(vec![path.to_path_buf()]);
117 }
118
119 if let Some(p) = find_cargo_toml_in_parent_dir(path) {
120 return Ok(vec![p]);
121 }
122
123 let entities = read_dir(path)?;
124 Ok(find_cargo_toml_in_child_dir(entities))
125 }
126
127 fn find_cargo_toml_in_parent_dir(path: &Path) -> Option<PathBuf> {
128 let mut curr = Some(path);
129 while let Some(path) = curr {
130 let candidate = path.join("Cargo.toml");
131 if candidate.exists() {
132 return Some(candidate);
133 }
134 curr = path.parent();
135 }
136
137 None
138 }
139
140 fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> {
141 // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
142 let mut valid_canditates = vec![];
143 for entity in entities.filter_map(Result::ok) {
144 let candidate = entity.path().join("Cargo.toml");
145 if candidate.exists() {
146 valid_canditates.push(candidate)
147 }
148 }
149 valid_canditates
150 }
151 }
152}
153
154impl ProjectWorkspace {
155 pub fn load(
156 root: ProjectRoot,
88 cargo_features: &CargoConfig, 157 cargo_features: &CargoConfig,
158 with_sysroot: bool,
89 ) -> Result<ProjectWorkspace> { 159 ) -> Result<ProjectWorkspace> {
90 match find_rust_project_json(path) { 160 let res = match root {
91 Some(json_path) => { 161 ProjectRoot::ProjectJson(project_json) => {
92 let file = File::open(&json_path) 162 let file = File::open(&project_json).with_context(|| {
93 .with_context(|| format!("Failed to open json file {}", json_path.display()))?; 163 format!("Failed to open json file {}", project_json.display())
164 })?;
94 let reader = BufReader::new(file); 165 let reader = BufReader::new(file);
95 Ok(ProjectWorkspace::Json { 166 ProjectWorkspace::Json {
96 project: from_reader(reader).with_context(|| { 167 project: from_reader(reader).with_context(|| {
97 format!("Failed to deserialize json file {}", json_path.display()) 168 format!("Failed to deserialize json file {}", project_json.display())
98 })?, 169 })?,
99 }) 170 }
100 } 171 }
101 None => { 172 ProjectRoot::CargoToml(cargo_toml) => {
102 let cargo_toml = find_cargo_toml(path).with_context(|| {
103 format!("Failed to find Cargo.toml for path {}", path.display())
104 })?;
105 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features) 173 let cargo = CargoWorkspace::from_cargo_metadata(&cargo_toml, cargo_features)
106 .with_context(|| { 174 .with_context(|| {
107 format!( 175 format!(
@@ -119,9 +187,11 @@ impl ProjectWorkspace {
119 } else { 187 } else {
120 Sysroot::default() 188 Sysroot::default()
121 }; 189 };
122 Ok(ProjectWorkspace::Cargo { cargo, sysroot }) 190 ProjectWorkspace::Cargo { cargo, sysroot }
123 } 191 }
124 } 192 };
193
194 Ok(res)
125 } 195 }
126 196
127 /// Returns the roots for the current `ProjectWorkspace` 197 /// Returns the roots for the current `ProjectWorkspace`
@@ -469,87 +539,6 @@ impl ProjectWorkspace {
469 } 539 }
470} 540}
471 541
472fn find_rust_project_json(path: &Path) -> Option<PathBuf> {
473 if path.ends_with("rust-project.json") {
474 return Some(path.to_path_buf());
475 }
476
477 let mut curr = Some(path);
478 while let Some(path) = curr {
479 let candidate = path.join("rust-project.json");
480 if candidate.exists() {
481 return Some(candidate);
482 }
483 curr = path.parent();
484 }
485
486 None
487}
488
489fn find_cargo_toml_in_parent_dir(path: &Path) -> Option<PathBuf> {
490 let mut curr = Some(path);
491 while let Some(path) = curr {
492 let candidate = path.join("Cargo.toml");
493 if candidate.exists() {
494 return Some(candidate);
495 }
496 curr = path.parent();
497 }
498
499 None
500}
501
502fn find_cargo_toml_in_child_dir(entities: ReadDir) -> Vec<PathBuf> {
503 // Only one level down to avoid cycles the easy way and stop a runaway scan with large projects
504 let mut valid_canditates = vec![];
505 for entity in entities.filter_map(Result::ok) {
506 let candidate = entity.path().join("Cargo.toml");
507 if candidate.exists() {
508 valid_canditates.push(candidate)
509 }
510 }
511 valid_canditates
512}
513
514fn find_cargo_toml(path: &Path) -> Result<PathBuf> {
515 if path.ends_with("Cargo.toml") {
516 return Ok(path.to_path_buf());
517 }
518
519 if let Some(p) = find_cargo_toml_in_parent_dir(path) {
520 return Ok(p);
521 }
522
523 let entities = match read_dir(path) {
524 Ok(entities) => entities,
525 Err(e) => {
526 return Err(CargoTomlNotFoundError {
527 searched_at: path.to_path_buf(),
528 reason: format!("file system error: {}", e),
529 }
530 .into());
531 }
532 };
533
534 let mut valid_canditates = find_cargo_toml_in_child_dir(entities);
535 match valid_canditates.len() {
536 1 => Ok(valid_canditates.remove(0)),
537 0 => Err(CargoTomlNotFoundError {
538 searched_at: path.to_path_buf(),
539 reason: "no Cargo.toml file found".to_string(),
540 }
541 .into()),
542 _ => Err(CargoTomlNotFoundError {
543 searched_at: path.to_path_buf(),
544 reason: format!(
545 "multiple equally valid Cargo.toml files found: {:?}",
546 valid_canditates
547 ),
548 }
549 .into()),
550 }
551}
552
553pub fn get_rustc_cfg_options() -> CfgOptions { 542pub fn get_rustc_cfg_options() -> CfgOptions {
554 let mut cfg_options = CfgOptions::default(); 543 let mut cfg_options = CfgOptions::default();
555 544
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 018be70ee..762f776fe 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -8,7 +8,7 @@ use crossbeam_channel::{unbounded, Receiver};
8use ra_db::{ExternSourceId, FileId, SourceRootId}; 8use ra_db::{ExternSourceId, FileId, SourceRootId};
9use ra_ide::{AnalysisChange, AnalysisHost}; 9use ra_ide::{AnalysisChange, AnalysisHost};
10use ra_project_model::{ 10use ra_project_model::{
11 get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectWorkspace, 11 get_rustc_cfg_options, CargoConfig, PackageRoot, ProcMacroClient, ProjectRoot, ProjectWorkspace,
12}; 12};
13use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; 13use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch};
14use rustc_hash::{FxHashMap, FxHashSet}; 14use rustc_hash::{FxHashMap, FxHashSet};
@@ -28,9 +28,11 @@ pub(crate) fn load_cargo(
28 with_proc_macro: bool, 28 with_proc_macro: bool,
29) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> { 29) -> Result<(AnalysisHost, FxHashMap<SourceRootId, PackageRoot>)> {
30 let root = std::env::current_dir()?.join(root); 30 let root = std::env::current_dir()?.join(root);
31 let ws = ProjectWorkspace::discover( 31 let root = ProjectRoot::discover_single(&root)?;
32 root.as_ref(), 32 let ws = ProjectWorkspace::load(
33 root,
33 &CargoConfig { load_out_dirs_from_check, ..Default::default() }, 34 &CargoConfig { load_out_dirs_from_check, ..Default::default() },
35 true,
34 )?; 36 )?;
35 37
36 let mut extern_dirs = FxHashSet::default(); 38 let mut extern_dirs = FxHashSet::default();
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index 8d1429196..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
17use crossbeam_channel::{never, select, unbounded, RecvError, Sender}; 17use crossbeam_channel::{never, select, unbounded, RecvError, Sender};
18use itertools::Itertools;
18use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response}; 19use lsp_server::{Connection, ErrorCode, Message, Notification, Request, RequestId, Response};
19use lsp_types::{ 20use lsp_types::{
20 NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams, 21 NumberOrString, WorkDoneProgress, WorkDoneProgressBegin, WorkDoneProgressCreateParams,
@@ -88,37 +89,46 @@ pub fn main_loop(ws_roots: Vec<PathBuf>, config: Config, connection: Connection)
88 89
89 let mut loop_state = LoopState::default(); 90 let mut loop_state = LoopState::default();
90 let mut world_state = { 91 let mut world_state = {
91 // FIXME: support dynamic workspace loading.
92 let workspaces = { 92 let workspaces = {
93 let mut loaded_workspaces = Vec::new(); 93 // FIXME: support dynamic workspace loading.
94 for ws_root in &ws_roots { 94 let mut visited = FxHashSet::default();
95 let workspace = ra_project_model::ProjectWorkspace::discover_with_sysroot( 95 let project_roots = ws_roots
96 ws_root.as_path(), 96 .iter()
97 config.with_sysroot, 97 .filter_map(|it| ra_project_model::ProjectRoot::discover(it).ok())
98 &config.cargo, 98 .flatten()
99 ); 99 .filter(|it| visited.insert(it.clone()))
100 match workspace { 100 .collect::<Vec<_>>();
101 Ok(workspace) => loaded_workspaces.push(workspace), 101
102 Err(e) => { 102 if project_roots.is_empty() && config.notifications.cargo_toml_not_found {
103 log::error!("loading workspace failed: {:?}", e); 103 show_message(
104 104 req::MessageType::Error,
105 if let Some(ra_project_model::CargoTomlNotFoundError { .. }) = 105 format!(
106 e.downcast_ref() 106 "rust-analyzer failed to discover workspace, no Cargo.toml found, dirs searched: {}",
107 { 107 ws_roots.iter().format_with(", ", |it, f| f(&it.display()))
108 if !config.notifications.cargo_toml_not_found { 108 ),
109 continue; 109 &connection.sender,
110 } 110 );
111 } 111 };
112 112
113 project_roots
114 .into_iter()
115 .filter_map(|root| {
116 ra_project_model::ProjectWorkspace::load(
117 root,
118 &config.cargo,
119 config.with_sysroot,
120 )
121 .map_err(|err| {
122 log::error!("failed to load workspace: {:#}", err);
113 show_message( 123 show_message(
114 req::MessageType::Error, 124 req::MessageType::Error,
115 format!("rust-analyzer failed to load workspace: {:?}", e), 125 format!("rust-analyzer failed to load workspace: {:#}", err),
116 &connection.sender, 126 &connection.sender,
117 ); 127 );
118 } 128 })
119 } 129 .ok()
120 } 130 })
121 loaded_workspaces 131 .collect::<Vec<_>>()
122 }; 132 };
123 133
124 let globs = config 134 let globs = config
diff --git a/xtask/tests/tidy-tests/main.rs b/xtask/tests/tidy-tests/main.rs
index 101ae19bd..ead642acc 100644
--- a/xtask/tests/tidy-tests/main.rs
+++ b/xtask/tests/tidy-tests/main.rs
@@ -35,7 +35,7 @@ fn check_todo(path: &Path, text: &str) {
35 } 35 }
36 if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") { 36 if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") {
37 panic!( 37 panic!(
38 "\nTODO markers should not be committed to the master branch,\n\ 38 "\nTODO markers or todo! macros should not be committed to the master branch,\n\
39 use FIXME instead\n\ 39 use FIXME instead\n\
40 {}\n", 40 {}\n",
41 path.display(), 41 path.display(),
@@ -47,9 +47,9 @@ fn check_trailing_ws(path: &Path, text: &str) {
47 if is_exclude_dir(path, &["test_data"]) { 47 if is_exclude_dir(path, &["test_data"]) {
48 return; 48 return;
49 } 49 }
50 for line in text.lines() { 50 for (line_number, line) in text.lines().enumerate() {
51 if line.chars().last().map(char::is_whitespace) == Some(true) { 51 if line.chars().last().map(char::is_whitespace) == Some(true) {
52 panic!("Trailing whitespace in {}", path.display()) 52 panic!("Trailing whitespace in {} at line {}", path.display(), line_number)
53 } 53 }
54 } 54 }
55} 55}