diff options
28 files changed, 517 insertions, 63 deletions
diff --git a/Cargo.lock b/Cargo.lock index 10ea55550..3bb1df05b 100644 --- a/Cargo.lock +++ b/Cargo.lock | |||
@@ -928,6 +928,7 @@ dependencies = [ | |||
928 | "ra_cfg", | 928 | "ra_cfg", |
929 | "ra_prof", | 929 | "ra_prof", |
930 | "ra_syntax", | 930 | "ra_syntax", |
931 | "ra_tt", | ||
931 | "relative-path", | 932 | "relative-path", |
932 | "rustc-hash", | 933 | "rustc-hash", |
933 | "salsa", | 934 | "salsa", |
@@ -1082,6 +1083,13 @@ dependencies = [ | |||
1082 | ] | 1083 | ] |
1083 | 1084 | ||
1084 | [[package]] | 1085 | [[package]] |
1086 | name = "ra_proc_macro" | ||
1087 | version = "0.1.0" | ||
1088 | dependencies = [ | ||
1089 | "ra_tt", | ||
1090 | ] | ||
1091 | |||
1092 | [[package]] | ||
1085 | name = "ra_prof" | 1093 | name = "ra_prof" |
1086 | version = "0.1.0" | 1094 | version = "0.1.0" |
1087 | dependencies = [ | 1095 | dependencies = [ |
@@ -1102,6 +1110,7 @@ dependencies = [ | |||
1102 | "ra_cargo_watch", | 1110 | "ra_cargo_watch", |
1103 | "ra_cfg", | 1111 | "ra_cfg", |
1104 | "ra_db", | 1112 | "ra_db", |
1113 | "ra_proc_macro", | ||
1105 | "rustc-hash", | 1114 | "rustc-hash", |
1106 | "serde", | 1115 | "serde", |
1107 | "serde_json", | 1116 | "serde_json", |
diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs index 62dcb3808..543224232 100644 --- a/crates/ra_assists/src/doc_tests/generated.rs +++ b/crates/ra_assists/src/doc_tests/generated.rs | |||
@@ -623,6 +623,30 @@ fn process(map: HashMap<String, String>) {} | |||
623 | } | 623 | } |
624 | 624 | ||
625 | #[test] | 625 | #[test] |
626 | fn doctest_replace_unwrap_with_match() { | ||
627 | check( | ||
628 | "replace_unwrap_with_match", | ||
629 | r#####" | ||
630 | enum Result<T, E> { Ok(T), Err(E) } | ||
631 | fn main() { | ||
632 | let x: Result<i32, i32> = Result::Ok(92); | ||
633 | let y = x.<|>unwrap(); | ||
634 | } | ||
635 | "#####, | ||
636 | r#####" | ||
637 | enum Result<T, E> { Ok(T), Err(E) } | ||
638 | fn main() { | ||
639 | let x: Result<i32, i32> = Result::Ok(92); | ||
640 | let y = match x { | ||
641 | Ok(a) => a, | ||
642 | _ => unreachable!(), | ||
643 | }; | ||
644 | } | ||
645 | "#####, | ||
646 | ) | ||
647 | } | ||
648 | |||
649 | #[test] | ||
626 | fn doctest_split_import() { | 650 | fn doctest_split_import() { |
627 | check( | 651 | check( |
628 | "split_import", | 652 | "split_import", |
diff --git a/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs new file mode 100644 index 000000000..62cb7a763 --- /dev/null +++ b/crates/ra_assists/src/handlers/replace_unwrap_with_match.rs | |||
@@ -0,0 +1,177 @@ | |||
1 | use std::iter; | ||
2 | |||
3 | use ra_syntax::{ | ||
4 | ast::{self, make}, | ||
5 | AstNode, | ||
6 | }; | ||
7 | |||
8 | use crate::{Assist, AssistCtx, AssistId}; | ||
9 | use ast::edit::IndentLevel; | ||
10 | |||
11 | // Assist: replace_unwrap_with_match | ||
12 | // | ||
13 | // Replaces `unwrap` a `match` expression. Works for Result and Option. | ||
14 | // | ||
15 | // ``` | ||
16 | // enum Result<T, E> { Ok(T), Err(E) } | ||
17 | // fn main() { | ||
18 | // let x: Result<i32, i32> = Result::Ok(92); | ||
19 | // let y = x.<|>unwrap(); | ||
20 | // } | ||
21 | // ``` | ||
22 | // -> | ||
23 | // ``` | ||
24 | // enum Result<T, E> { Ok(T), Err(E) } | ||
25 | // fn main() { | ||
26 | // let x: Result<i32, i32> = Result::Ok(92); | ||
27 | // let y = match x { | ||
28 | // Ok(a) => a, | ||
29 | // _ => unreachable!(), | ||
30 | // }; | ||
31 | // } | ||
32 | // ``` | ||
33 | pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option<Assist> { | ||
34 | let method_call: ast::MethodCallExpr = ctx.find_node_at_offset()?; | ||
35 | let name = method_call.name_ref()?; | ||
36 | if name.text() != "unwrap" { | ||
37 | return None; | ||
38 | } | ||
39 | let caller = method_call.expr()?; | ||
40 | let ty = ctx.sema.type_of_expr(&caller)?; | ||
41 | |||
42 | let type_name = ty.as_adt()?.name(ctx.sema.db).to_string(); | ||
43 | |||
44 | for (unwrap_type, variant_name) in [("Result", "Ok"), ("Option", "Some")].iter() { | ||
45 | if &type_name == unwrap_type { | ||
46 | return ctx.add_assist( | ||
47 | AssistId("replace_unwrap_with_match"), | ||
48 | "Replace unwrap with match", | ||
49 | |edit| { | ||
50 | let ok_path = | ||
51 | make::path_unqualified(make::path_segment(make::name_ref(variant_name))); | ||
52 | let it = make::bind_pat(make::name("a")).into(); | ||
53 | let ok_tuple = make::tuple_struct_pat(ok_path, iter::once(it)).into(); | ||
54 | |||
55 | let bind_path = make::path_unqualified(make::path_segment(make::name_ref("a"))); | ||
56 | let ok_arm = make::match_arm(iter::once(ok_tuple), make::expr_path(bind_path)); | ||
57 | |||
58 | let unreachable_call = make::unreachable_macro_call().into(); | ||
59 | let err_arm = make::match_arm( | ||
60 | iter::once(make::placeholder_pat().into()), | ||
61 | unreachable_call, | ||
62 | ); | ||
63 | |||
64 | let match_arm_list = make::match_arm_list(vec![ok_arm, err_arm]); | ||
65 | let match_expr = make::expr_match(caller.clone(), match_arm_list); | ||
66 | let match_expr = | ||
67 | IndentLevel::from_node(method_call.syntax()).increase_indent(match_expr); | ||
68 | |||
69 | edit.target(method_call.syntax().text_range()); | ||
70 | edit.set_cursor(caller.syntax().text_range().start()); | ||
71 | edit.replace_ast::<ast::Expr>(method_call.into(), match_expr); | ||
72 | }, | ||
73 | ); | ||
74 | } | ||
75 | } | ||
76 | None | ||
77 | } | ||
78 | |||
79 | #[cfg(test)] | ||
80 | mod tests { | ||
81 | use super::*; | ||
82 | use crate::helpers::{check_assist, check_assist_target}; | ||
83 | |||
84 | #[test] | ||
85 | fn test_replace_result_unwrap_with_match() { | ||
86 | check_assist( | ||
87 | replace_unwrap_with_match, | ||
88 | r" | ||
89 | enum Result<T, E> { Ok(T), Err(E) } | ||
90 | fn i<T>(a: T) -> T { a } | ||
91 | fn main() { | ||
92 | let x: Result<i32, i32> = Result::Ok(92); | ||
93 | let y = i(x).<|>unwrap(); | ||
94 | } | ||
95 | ", | ||
96 | r" | ||
97 | enum Result<T, E> { Ok(T), Err(E) } | ||
98 | fn i<T>(a: T) -> T { a } | ||
99 | fn main() { | ||
100 | let x: Result<i32, i32> = Result::Ok(92); | ||
101 | let y = <|>match i(x) { | ||
102 | Ok(a) => a, | ||
103 | _ => unreachable!(), | ||
104 | }; | ||
105 | } | ||
106 | ", | ||
107 | ) | ||
108 | } | ||
109 | |||
110 | #[test] | ||
111 | fn test_replace_option_unwrap_with_match() { | ||
112 | check_assist( | ||
113 | replace_unwrap_with_match, | ||
114 | r" | ||
115 | enum Option<T> { Some(T), None } | ||
116 | fn i<T>(a: T) -> T { a } | ||
117 | fn main() { | ||
118 | let x = Option::Some(92); | ||
119 | let y = i(x).<|>unwrap(); | ||
120 | } | ||
121 | ", | ||
122 | r" | ||
123 | enum Option<T> { Some(T), None } | ||
124 | fn i<T>(a: T) -> T { a } | ||
125 | fn main() { | ||
126 | let x = Option::Some(92); | ||
127 | let y = <|>match i(x) { | ||
128 | Some(a) => a, | ||
129 | _ => unreachable!(), | ||
130 | }; | ||
131 | } | ||
132 | ", | ||
133 | ); | ||
134 | } | ||
135 | |||
136 | #[test] | ||
137 | fn test_replace_result_unwrap_with_match_chaining() { | ||
138 | check_assist( | ||
139 | replace_unwrap_with_match, | ||
140 | r" | ||
141 | enum Result<T, E> { Ok(T), Err(E) } | ||
142 | fn i<T>(a: T) -> T { a } | ||
143 | fn main() { | ||
144 | let x: Result<i32, i32> = Result::Ok(92); | ||
145 | let y = i(x).<|>unwrap().count_zeroes(); | ||
146 | } | ||
147 | ", | ||
148 | r" | ||
149 | enum Result<T, E> { Ok(T), Err(E) } | ||
150 | fn i<T>(a: T) -> T { a } | ||
151 | fn main() { | ||
152 | let x: Result<i32, i32> = Result::Ok(92); | ||
153 | let y = <|>match i(x) { | ||
154 | Ok(a) => a, | ||
155 | _ => unreachable!(), | ||
156 | }.count_zeroes(); | ||
157 | } | ||
158 | ", | ||
159 | ) | ||
160 | } | ||
161 | |||
162 | #[test] | ||
163 | fn replace_unwrap_with_match_target() { | ||
164 | check_assist_target( | ||
165 | replace_unwrap_with_match, | ||
166 | r" | ||
167 | enum Option<T> { Some(T), None } | ||
168 | fn i<T>(a: T) -> T { a } | ||
169 | fn main() { | ||
170 | let x = Option::Some(92); | ||
171 | let y = i(x).<|>unwrap(); | ||
172 | } | ||
173 | ", | ||
174 | r"i(x).unwrap()", | ||
175 | ); | ||
176 | } | ||
177 | } | ||
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index bcc9b3f10..becd5e99d 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -119,6 +119,7 @@ mod handlers { | |||
119 | mod remove_mut; | 119 | mod remove_mut; |
120 | mod replace_if_let_with_match; | 120 | mod replace_if_let_with_match; |
121 | mod replace_qualified_name_with_use; | 121 | mod replace_qualified_name_with_use; |
122 | mod replace_unwrap_with_match; | ||
122 | mod split_import; | 123 | mod split_import; |
123 | 124 | ||
124 | pub(crate) fn all() -> &'static [AssistHandler] { | 125 | pub(crate) fn all() -> &'static [AssistHandler] { |
@@ -154,6 +155,7 @@ mod handlers { | |||
154 | remove_mut::remove_mut, | 155 | remove_mut::remove_mut, |
155 | replace_if_let_with_match::replace_if_let_with_match, | 156 | replace_if_let_with_match::replace_if_let_with_match, |
156 | replace_qualified_name_with_use::replace_qualified_name_with_use, | 157 | replace_qualified_name_with_use::replace_qualified_name_with_use, |
158 | replace_unwrap_with_match::replace_unwrap_with_match, | ||
157 | split_import::split_import, | 159 | split_import::split_import, |
158 | ] | 160 | ] |
159 | } | 161 | } |
diff --git a/crates/ra_db/Cargo.toml b/crates/ra_db/Cargo.toml index 878c22ba9..8ab409158 100644 --- a/crates/ra_db/Cargo.toml +++ b/crates/ra_db/Cargo.toml | |||
@@ -15,4 +15,5 @@ rustc-hash = "1.1.0" | |||
15 | ra_syntax = { path = "../ra_syntax" } | 15 | ra_syntax = { path = "../ra_syntax" } |
16 | ra_cfg = { path = "../ra_cfg" } | 16 | ra_cfg = { path = "../ra_cfg" } |
17 | ra_prof = { path = "../ra_prof" } | 17 | ra_prof = { path = "../ra_prof" } |
18 | ra_tt = { path = "../ra_tt" } | ||
18 | test_utils = { path = "../test_utils" } | 19 | test_utils = { path = "../test_utils" } |
diff --git a/crates/ra_db/src/fixture.rs b/crates/ra_db/src/fixture.rs index 5e3e9203c..7777ce81e 100644 --- a/crates/ra_db/src/fixture.rs +++ b/crates/ra_db/src/fixture.rs | |||
@@ -70,6 +70,7 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId | |||
70 | meta.cfg, | 70 | meta.cfg, |
71 | meta.env, | 71 | meta.env, |
72 | Default::default(), | 72 | Default::default(), |
73 | Default::default(), | ||
73 | ); | 74 | ); |
74 | crate_graph | 75 | crate_graph |
75 | } else { | 76 | } else { |
@@ -81,6 +82,7 @@ fn with_single_file(db: &mut dyn SourceDatabaseExt, ra_fixture: &str) -> FileId | |||
81 | CfgOptions::default(), | 82 | CfgOptions::default(), |
82 | Env::default(), | 83 | Env::default(), |
83 | Default::default(), | 84 | Default::default(), |
85 | Default::default(), | ||
84 | ); | 86 | ); |
85 | crate_graph | 87 | crate_graph |
86 | }; | 88 | }; |
@@ -130,6 +132,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit | |||
130 | meta.cfg, | 132 | meta.cfg, |
131 | meta.env, | 133 | meta.env, |
132 | Default::default(), | 134 | Default::default(), |
135 | Default::default(), | ||
133 | ); | 136 | ); |
134 | let prev = crates.insert(krate.clone(), crate_id); | 137 | let prev = crates.insert(krate.clone(), crate_id); |
135 | assert!(prev.is_none()); | 138 | assert!(prev.is_none()); |
@@ -167,6 +170,7 @@ fn with_files(db: &mut dyn SourceDatabaseExt, fixture: &str) -> Option<FilePosit | |||
167 | CfgOptions::default(), | 170 | CfgOptions::default(), |
168 | Env::default(), | 171 | Env::default(), |
169 | Default::default(), | 172 | Default::default(), |
173 | Default::default(), | ||
170 | ); | 174 | ); |
171 | } else { | 175 | } else { |
172 | for (from, to) in crate_deps { | 176 | for (from, to) in crate_deps { |
diff --git a/crates/ra_db/src/input.rs b/crates/ra_db/src/input.rs index e371f849d..5ddce98c6 100644 --- a/crates/ra_db/src/input.rs +++ b/crates/ra_db/src/input.rs | |||
@@ -10,6 +10,7 @@ use std::{ | |||
10 | fmt, ops, | 10 | fmt, ops, |
11 | path::{Path, PathBuf}, | 11 | path::{Path, PathBuf}, |
12 | str::FromStr, | 12 | str::FromStr, |
13 | sync::Arc, | ||
13 | }; | 14 | }; |
14 | 15 | ||
15 | use ra_cfg::CfgOptions; | 16 | use ra_cfg::CfgOptions; |
@@ -19,6 +20,7 @@ use rustc_hash::FxHashSet; | |||
19 | 20 | ||
20 | use crate::{RelativePath, RelativePathBuf}; | 21 | use crate::{RelativePath, RelativePathBuf}; |
21 | use fmt::Display; | 22 | use fmt::Display; |
23 | use ra_tt::TokenExpander; | ||
22 | 24 | ||
23 | /// `FileId` is an integer which uniquely identifies a file. File paths are | 25 | /// `FileId` is an integer which uniquely identifies a file. File paths are |
24 | /// messy and system-dependent, so most of the code should work directly with | 26 | /// messy and system-dependent, so most of the code should work directly with |
@@ -115,6 +117,22 @@ impl Display for CrateName { | |||
115 | } | 117 | } |
116 | } | 118 | } |
117 | 119 | ||
120 | #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] | ||
121 | pub struct ProcMacroId(pub u32); | ||
122 | |||
123 | #[derive(Debug, Clone)] | ||
124 | pub struct ProcMacro { | ||
125 | pub name: SmolStr, | ||
126 | pub expander: Arc<dyn TokenExpander>, | ||
127 | } | ||
128 | |||
129 | impl Eq for ProcMacro {} | ||
130 | impl PartialEq for ProcMacro { | ||
131 | fn eq(&self, other: &ProcMacro) -> bool { | ||
132 | self.name == other.name && Arc::ptr_eq(&self.expander, &other.expander) | ||
133 | } | ||
134 | } | ||
135 | |||
118 | #[derive(Debug, Clone, PartialEq, Eq)] | 136 | #[derive(Debug, Clone, PartialEq, Eq)] |
119 | pub struct CrateData { | 137 | pub struct CrateData { |
120 | pub root_file_id: FileId, | 138 | pub root_file_id: FileId, |
@@ -127,6 +145,7 @@ pub struct CrateData { | |||
127 | pub env: Env, | 145 | pub env: Env, |
128 | pub extern_source: ExternSource, | 146 | pub extern_source: ExternSource, |
129 | pub dependencies: Vec<Dependency>, | 147 | pub dependencies: Vec<Dependency>, |
148 | pub proc_macro: Vec<ProcMacro>, | ||
130 | } | 149 | } |
131 | 150 | ||
132 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | 151 | #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
@@ -166,7 +185,11 @@ impl CrateGraph { | |||
166 | cfg_options: CfgOptions, | 185 | cfg_options: CfgOptions, |
167 | env: Env, | 186 | env: Env, |
168 | extern_source: ExternSource, | 187 | extern_source: ExternSource, |
188 | proc_macro: Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)>, | ||
169 | ) -> CrateId { | 189 | ) -> CrateId { |
190 | let proc_macro = | ||
191 | proc_macro.into_iter().map(|(name, it)| ProcMacro { name, expander: it }).collect(); | ||
192 | |||
170 | let data = CrateData { | 193 | let data = CrateData { |
171 | root_file_id: file_id, | 194 | root_file_id: file_id, |
172 | edition, | 195 | edition, |
@@ -174,6 +197,7 @@ impl CrateGraph { | |||
174 | cfg_options, | 197 | cfg_options, |
175 | env, | 198 | env, |
176 | extern_source, | 199 | extern_source, |
200 | proc_macro, | ||
177 | dependencies: Vec::new(), | 201 | dependencies: Vec::new(), |
178 | }; | 202 | }; |
179 | let crate_id = CrateId(self.arena.len() as u32); | 203 | let crate_id = CrateId(self.arena.len() as u32); |
@@ -345,6 +369,7 @@ mod tests { | |||
345 | CfgOptions::default(), | 369 | CfgOptions::default(), |
346 | Env::default(), | 370 | Env::default(), |
347 | Default::default(), | 371 | Default::default(), |
372 | Default::default(), | ||
348 | ); | 373 | ); |
349 | let crate2 = graph.add_crate_root( | 374 | let crate2 = graph.add_crate_root( |
350 | FileId(2u32), | 375 | FileId(2u32), |
@@ -353,6 +378,7 @@ mod tests { | |||
353 | CfgOptions::default(), | 378 | CfgOptions::default(), |
354 | Env::default(), | 379 | Env::default(), |
355 | Default::default(), | 380 | Default::default(), |
381 | Default::default(), | ||
356 | ); | 382 | ); |
357 | let crate3 = graph.add_crate_root( | 383 | let crate3 = graph.add_crate_root( |
358 | FileId(3u32), | 384 | FileId(3u32), |
@@ -361,6 +387,7 @@ mod tests { | |||
361 | CfgOptions::default(), | 387 | CfgOptions::default(), |
362 | Env::default(), | 388 | Env::default(), |
363 | Default::default(), | 389 | Default::default(), |
390 | Default::default(), | ||
364 | ); | 391 | ); |
365 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); | 392 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); |
366 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); | 393 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); |
@@ -377,6 +404,7 @@ mod tests { | |||
377 | CfgOptions::default(), | 404 | CfgOptions::default(), |
378 | Env::default(), | 405 | Env::default(), |
379 | Default::default(), | 406 | Default::default(), |
407 | Default::default(), | ||
380 | ); | 408 | ); |
381 | let crate2 = graph.add_crate_root( | 409 | let crate2 = graph.add_crate_root( |
382 | FileId(2u32), | 410 | FileId(2u32), |
@@ -385,6 +413,7 @@ mod tests { | |||
385 | CfgOptions::default(), | 413 | CfgOptions::default(), |
386 | Env::default(), | 414 | Env::default(), |
387 | Default::default(), | 415 | Default::default(), |
416 | Default::default(), | ||
388 | ); | 417 | ); |
389 | let crate3 = graph.add_crate_root( | 418 | let crate3 = graph.add_crate_root( |
390 | FileId(3u32), | 419 | FileId(3u32), |
@@ -393,6 +422,7 @@ mod tests { | |||
393 | CfgOptions::default(), | 422 | CfgOptions::default(), |
394 | Env::default(), | 423 | Env::default(), |
395 | Default::default(), | 424 | Default::default(), |
425 | Default::default(), | ||
396 | ); | 426 | ); |
397 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); | 427 | assert!(graph.add_dep(crate1, CrateName::new("crate2").unwrap(), crate2).is_ok()); |
398 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); | 428 | assert!(graph.add_dep(crate2, CrateName::new("crate3").unwrap(), crate3).is_ok()); |
@@ -408,6 +438,7 @@ mod tests { | |||
408 | CfgOptions::default(), | 438 | CfgOptions::default(), |
409 | Env::default(), | 439 | Env::default(), |
410 | Default::default(), | 440 | Default::default(), |
441 | Default::default(), | ||
411 | ); | 442 | ); |
412 | let crate2 = graph.add_crate_root( | 443 | let crate2 = graph.add_crate_root( |
413 | FileId(2u32), | 444 | FileId(2u32), |
@@ -416,6 +447,7 @@ mod tests { | |||
416 | CfgOptions::default(), | 447 | CfgOptions::default(), |
417 | Env::default(), | 448 | Env::default(), |
418 | Default::default(), | 449 | Default::default(), |
450 | Default::default(), | ||
419 | ); | 451 | ); |
420 | assert!(graph | 452 | assert!(graph |
421 | .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2) | 453 | .add_dep(crate1, CrateName::normalize_dashes("crate-name-with-dashes"), crate2) |
diff --git a/crates/ra_db/src/lib.rs b/crates/ra_db/src/lib.rs index bac24e218..a06f59c14 100644 --- a/crates/ra_db/src/lib.rs +++ b/crates/ra_db/src/lib.rs | |||
@@ -12,7 +12,7 @@ pub use crate::{ | |||
12 | cancellation::Canceled, | 12 | cancellation::Canceled, |
13 | input::{ | 13 | input::{ |
14 | CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, ExternSourceId, | 14 | CrateGraph, CrateId, CrateName, Dependency, Edition, Env, ExternSource, ExternSourceId, |
15 | FileId, SourceRoot, SourceRootId, | 15 | FileId, ProcMacroId, SourceRoot, SourceRootId, |
16 | }, | 16 | }, |
17 | }; | 17 | }; |
18 | pub use relative_path::{RelativePath, RelativePathBuf}; | 18 | pub use relative_path::{RelativePath, RelativePathBuf}; |
diff --git a/crates/ra_hir_def/src/nameres/collector.rs b/crates/ra_hir_def/src/nameres/collector.rs index 9c125f32f..8fe3f8617 100644 --- a/crates/ra_hir_def/src/nameres/collector.rs +++ b/crates/ra_hir_def/src/nameres/collector.rs | |||
@@ -11,7 +11,7 @@ use hir_expand::{ | |||
11 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, | 11 | HirFileId, MacroCallId, MacroDefId, MacroDefKind, |
12 | }; | 12 | }; |
13 | use ra_cfg::CfgOptions; | 13 | use ra_cfg::CfgOptions; |
14 | use ra_db::{CrateId, FileId}; | 14 | use ra_db::{CrateId, FileId, ProcMacroId}; |
15 | use ra_syntax::ast; | 15 | use ra_syntax::ast; |
16 | use rustc_hash::FxHashMap; | 16 | use rustc_hash::FxHashMap; |
17 | use test_utils::tested_by; | 17 | use test_utils::tested_by; |
@@ -53,6 +53,16 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> Cr | |||
53 | } | 53 | } |
54 | 54 | ||
55 | let cfg_options = &crate_graph[def_map.krate].cfg_options; | 55 | let cfg_options = &crate_graph[def_map.krate].cfg_options; |
56 | let proc_macros = &crate_graph[def_map.krate].proc_macro; | ||
57 | let proc_macros = proc_macros | ||
58 | .iter() | ||
59 | .enumerate() | ||
60 | .map(|(idx, it)| { | ||
61 | // FIXME: a hacky way to create a Name from string. | ||
62 | let name = tt::Ident { text: it.name.clone(), id: tt::TokenId::unspecified() }; | ||
63 | (name.as_name(), ProcMacroExpander::new(def_map.krate, ProcMacroId(idx as u32))) | ||
64 | }) | ||
65 | .collect(); | ||
56 | 66 | ||
57 | let mut collector = DefCollector { | 67 | let mut collector = DefCollector { |
58 | db, | 68 | db, |
@@ -65,9 +75,7 @@ pub(super) fn collect_defs(db: &dyn DefDatabase, mut def_map: CrateDefMap) -> Cr | |||
65 | unexpanded_attribute_macros: Vec::new(), | 75 | unexpanded_attribute_macros: Vec::new(), |
66 | mod_dirs: FxHashMap::default(), | 76 | mod_dirs: FxHashMap::default(), |
67 | cfg_options, | 77 | cfg_options, |
68 | 78 | proc_macros, | |
69 | // FIXME: pass proc-macro from crate-graph | ||
70 | proc_macros: Default::default(), | ||
71 | }; | 79 | }; |
72 | collector.collect(); | 80 | collector.collect(); |
73 | collector.finish() | 81 | collector.finish() |
diff --git a/crates/ra_hir_expand/src/proc_macro.rs b/crates/ra_hir_expand/src/proc_macro.rs index a8dee2052..4d270e0de 100644 --- a/crates/ra_hir_expand/src/proc_macro.rs +++ b/crates/ra_hir_expand/src/proc_macro.rs | |||
@@ -1,33 +1,32 @@ | |||
1 | //! Proc Macro Expander stub | 1 | //! Proc Macro Expander stub |
2 | 2 | ||
3 | use crate::{db::AstDatabase, LazyMacroId, MacroCallKind, MacroCallLoc}; | 3 | use crate::{db::AstDatabase, LazyMacroId}; |
4 | use ra_db::CrateId; | 4 | use ra_db::{CrateId, ProcMacroId}; |
5 | 5 | ||
6 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] | 6 | #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] |
7 | pub struct ProcMacroExpander { | 7 | pub struct ProcMacroExpander { |
8 | krate: CrateId, | 8 | krate: CrateId, |
9 | proc_macro_id: ProcMacroId, | ||
9 | } | 10 | } |
10 | 11 | ||
11 | impl ProcMacroExpander { | 12 | impl ProcMacroExpander { |
12 | pub fn new(krate: CrateId) -> ProcMacroExpander { | 13 | pub fn new(krate: CrateId, proc_macro_id: ProcMacroId) -> ProcMacroExpander { |
13 | ProcMacroExpander { krate } | 14 | ProcMacroExpander { krate, proc_macro_id } |
14 | } | 15 | } |
15 | 16 | ||
16 | pub fn expand( | 17 | pub fn expand( |
17 | &self, | 18 | &self, |
18 | db: &dyn AstDatabase, | 19 | db: &dyn AstDatabase, |
19 | id: LazyMacroId, | 20 | _id: LazyMacroId, |
20 | _tt: &tt::Subtree, | 21 | tt: &tt::Subtree, |
21 | ) -> Result<tt::Subtree, mbe::ExpandError> { | 22 | ) -> Result<tt::Subtree, mbe::ExpandError> { |
22 | let loc: MacroCallLoc = db.lookup_intern_macro(id); | 23 | let krate_graph = db.crate_graph(); |
23 | let name = match loc.kind { | 24 | let proc_macro = krate_graph[self.krate] |
24 | MacroCallKind::FnLike(_) => return Err(mbe::ExpandError::ConversionError), | 25 | .proc_macro |
25 | MacroCallKind::Attr(_, name) => name, | 26 | .get(self.proc_macro_id.0 as usize) |
26 | }; | 27 | .clone() |
28 | .ok_or_else(|| mbe::ExpandError::ConversionError)?; | ||
27 | 29 | ||
28 | log::debug!("Proc-macro-expanding name = {}", name); | 30 | proc_macro.expander.expand(&tt, None).map_err(mbe::ExpandError::from) |
29 | |||
30 | // Return nothing for now | ||
31 | return Ok(tt::Subtree::default()); | ||
32 | } | 31 | } |
33 | } | 32 | } |
diff --git a/crates/ra_ide/src/lib.rs b/crates/ra_ide/src/lib.rs index 5ab06c6cf..e43414985 100644 --- a/crates/ra_ide/src/lib.rs +++ b/crates/ra_ide/src/lib.rs | |||
@@ -213,6 +213,7 @@ impl Analysis { | |||
213 | cfg_options, | 213 | cfg_options, |
214 | Env::default(), | 214 | Env::default(), |
215 | Default::default(), | 215 | Default::default(), |
216 | Default::default(), | ||
216 | ); | 217 | ); |
217 | change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); | 218 | change.add_file(source_root, file_id, "main.rs".into(), Arc::new(text)); |
218 | change.set_crate_graph(crate_graph); | 219 | change.set_crate_graph(crate_graph); |
diff --git a/crates/ra_ide/src/mock_analysis.rs b/crates/ra_ide/src/mock_analysis.rs index 2cf77a31f..2c13f206a 100644 --- a/crates/ra_ide/src/mock_analysis.rs +++ b/crates/ra_ide/src/mock_analysis.rs | |||
@@ -103,6 +103,7 @@ impl MockAnalysis { | |||
103 | cfg_options, | 103 | cfg_options, |
104 | Env::default(), | 104 | Env::default(), |
105 | Default::default(), | 105 | Default::default(), |
106 | Default::default(), | ||
106 | )); | 107 | )); |
107 | } else if path.ends_with("/lib.rs") { | 108 | } else if path.ends_with("/lib.rs") { |
108 | let crate_name = path.parent().unwrap().file_name().unwrap(); | 109 | let crate_name = path.parent().unwrap().file_name().unwrap(); |
@@ -113,6 +114,7 @@ impl MockAnalysis { | |||
113 | cfg_options, | 114 | cfg_options, |
114 | Env::default(), | 115 | Env::default(), |
115 | Default::default(), | 116 | Default::default(), |
117 | Default::default(), | ||
116 | ); | 118 | ); |
117 | if let Some(root_crate) = root_crate { | 119 | if let Some(root_crate) = root_crate { |
118 | crate_graph | 120 | crate_graph |
diff --git a/crates/ra_ide/src/parent_module.rs b/crates/ra_ide/src/parent_module.rs index 76d130b9b..958b92bed 100644 --- a/crates/ra_ide/src/parent_module.rs +++ b/crates/ra_ide/src/parent_module.rs | |||
@@ -137,6 +137,7 @@ mod tests { | |||
137 | CfgOptions::default(), | 137 | CfgOptions::default(), |
138 | Env::default(), | 138 | Env::default(), |
139 | Default::default(), | 139 | Default::default(), |
140 | Default::default(), | ||
140 | ); | 141 | ); |
141 | let mut change = AnalysisChange::new(); | 142 | let mut change = AnalysisChange::new(); |
142 | change.set_crate_graph(crate_graph); | 143 | change.set_crate_graph(crate_graph); |
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index 6a9037bfc..535b7daa0 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs | |||
@@ -28,6 +28,13 @@ pub enum ExpandError { | |||
28 | BindingError(String), | 28 | BindingError(String), |
29 | ConversionError, | 29 | ConversionError, |
30 | InvalidRepeat, | 30 | InvalidRepeat, |
31 | ProcMacroError(tt::ExpansionError), | ||
32 | } | ||
33 | |||
34 | impl From<tt::ExpansionError> for ExpandError { | ||
35 | fn from(it: tt::ExpansionError) -> Self { | ||
36 | ExpandError::ProcMacroError(it) | ||
37 | } | ||
31 | } | 38 | } |
32 | 39 | ||
33 | pub use crate::syntax_bridge::{ | 40 | pub use crate::syntax_bridge::{ |
diff --git a/crates/ra_proc_macro/Cargo.toml b/crates/ra_proc_macro/Cargo.toml new file mode 100644 index 000000000..bc2c37296 --- /dev/null +++ b/crates/ra_proc_macro/Cargo.toml | |||
@@ -0,0 +1,12 @@ | |||
1 | [package] | ||
2 | edition = "2018" | ||
3 | name = "ra_proc_macro" | ||
4 | version = "0.1.0" | ||
5 | authors = ["rust-analyzer developers"] | ||
6 | publish = false | ||
7 | |||
8 | [lib] | ||
9 | doctest = false | ||
10 | |||
11 | [dependencies] | ||
12 | ra_tt = { path = "../ra_tt" } | ||
diff --git a/crates/ra_proc_macro/src/lib.rs b/crates/ra_proc_macro/src/lib.rs new file mode 100644 index 000000000..5e21dd487 --- /dev/null +++ b/crates/ra_proc_macro/src/lib.rs | |||
@@ -0,0 +1,59 @@ | |||
1 | //! Client-side Proc-Macro crate | ||
2 | //! | ||
3 | //! We separate proc-macro expanding logic to an extern program to allow | ||
4 | //! different implementations (e.g. wasm or dylib loading). And this crate | ||
5 | //! is used to provide basic infrastructure for communication between two | ||
6 | //! processes: Client (RA itself), Server (the external program) | ||
7 | |||
8 | use ra_tt::{SmolStr, Subtree}; | ||
9 | use std::{ | ||
10 | path::{Path, PathBuf}, | ||
11 | sync::Arc, | ||
12 | }; | ||
13 | |||
14 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
15 | pub struct ProcMacroProcessExpander { | ||
16 | process: Arc<ProcMacroProcessSrv>, | ||
17 | name: SmolStr, | ||
18 | } | ||
19 | |||
20 | impl ra_tt::TokenExpander for ProcMacroProcessExpander { | ||
21 | fn expand( | ||
22 | &self, | ||
23 | _subtree: &Subtree, | ||
24 | _attr: Option<&Subtree>, | ||
25 | ) -> Result<Subtree, ra_tt::ExpansionError> { | ||
26 | // FIXME: do nothing for now | ||
27 | Ok(Subtree::default()) | ||
28 | } | ||
29 | } | ||
30 | |||
31 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
32 | pub struct ProcMacroProcessSrv { | ||
33 | path: PathBuf, | ||
34 | } | ||
35 | |||
36 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
37 | pub enum ProcMacroClient { | ||
38 | Process { process: Arc<ProcMacroProcessSrv> }, | ||
39 | Dummy, | ||
40 | } | ||
41 | |||
42 | impl ProcMacroClient { | ||
43 | pub fn extern_process(process_path: &Path) -> ProcMacroClient { | ||
44 | let process = ProcMacroProcessSrv { path: process_path.into() }; | ||
45 | ProcMacroClient::Process { process: Arc::new(process) } | ||
46 | } | ||
47 | |||
48 | pub fn dummy() -> ProcMacroClient { | ||
49 | ProcMacroClient::Dummy | ||
50 | } | ||
51 | |||
52 | pub fn by_dylib_path( | ||
53 | &self, | ||
54 | _dylib_path: &Path, | ||
55 | ) -> Vec<(SmolStr, Arc<dyn ra_tt::TokenExpander>)> { | ||
56 | // FIXME: return empty for now | ||
57 | vec![] | ||
58 | } | ||
59 | } | ||
diff --git a/crates/ra_project_model/Cargo.toml b/crates/ra_project_model/Cargo.toml index 22300548a..cdcdd63c9 100644 --- a/crates/ra_project_model/Cargo.toml +++ b/crates/ra_project_model/Cargo.toml | |||
@@ -17,6 +17,7 @@ ra_arena = { path = "../ra_arena" } | |||
17 | ra_db = { path = "../ra_db" } | 17 | ra_db = { path = "../ra_db" } |
18 | ra_cfg = { path = "../ra_cfg" } | 18 | ra_cfg = { path = "../ra_cfg" } |
19 | ra_cargo_watch = { path = "../ra_cargo_watch" } | 19 | ra_cargo_watch = { path = "../ra_cargo_watch" } |
20 | ra_proc_macro = { path = "../ra_proc_macro" } | ||
20 | 21 | ||
21 | serde = { version = "1.0.104", features = ["derive"] } | 22 | serde = { version = "1.0.104", features = ["derive"] } |
22 | serde_json = "1.0.48" | 23 | serde_json = "1.0.48" |
diff --git a/crates/ra_project_model/src/cargo_workspace.rs b/crates/ra_project_model/src/cargo_workspace.rs index c7f9bd873..291594e2a 100644 --- a/crates/ra_project_model/src/cargo_workspace.rs +++ b/crates/ra_project_model/src/cargo_workspace.rs | |||
@@ -83,6 +83,7 @@ pub struct PackageData { | |||
83 | pub edition: Edition, | 83 | pub edition: Edition, |
84 | pub features: Vec<String>, | 84 | pub features: Vec<String>, |
85 | pub out_dir: Option<PathBuf>, | 85 | pub out_dir: Option<PathBuf>, |
86 | pub proc_macro_dylib_path: Option<PathBuf>, | ||
86 | } | 87 | } |
87 | 88 | ||
88 | #[derive(Debug, Clone)] | 89 | #[derive(Debug, Clone)] |
@@ -158,8 +159,11 @@ impl CargoWorkspace { | |||
158 | })?; | 159 | })?; |
159 | 160 | ||
160 | let mut out_dir_by_id = FxHashMap::default(); | 161 | let mut out_dir_by_id = FxHashMap::default(); |
162 | let mut proc_macro_dylib_paths = FxHashMap::default(); | ||
161 | if cargo_features.load_out_dirs_from_check { | 163 | if cargo_features.load_out_dirs_from_check { |
162 | out_dir_by_id = load_out_dirs(cargo_toml, cargo_features); | 164 | let resources = load_extern_resources(cargo_toml, cargo_features); |
165 | out_dir_by_id = resources.out_dirs; | ||
166 | proc_macro_dylib_paths = resources.proc_dylib_paths; | ||
163 | } | 167 | } |
164 | 168 | ||
165 | let mut pkg_by_id = FxHashMap::default(); | 169 | let mut pkg_by_id = FxHashMap::default(); |
@@ -183,6 +187,7 @@ impl CargoWorkspace { | |||
183 | dependencies: Vec::new(), | 187 | dependencies: Vec::new(), |
184 | features: Vec::new(), | 188 | features: Vec::new(), |
185 | out_dir: out_dir_by_id.get(&id).cloned(), | 189 | out_dir: out_dir_by_id.get(&id).cloned(), |
190 | proc_macro_dylib_path: proc_macro_dylib_paths.get(&id).cloned(), | ||
186 | }); | 191 | }); |
187 | let pkg_data = &mut packages[pkg]; | 192 | let pkg_data = &mut packages[pkg]; |
188 | pkg_by_id.insert(id, pkg); | 193 | pkg_by_id.insert(id, pkg); |
@@ -246,10 +251,13 @@ impl CargoWorkspace { | |||
246 | } | 251 | } |
247 | } | 252 | } |
248 | 253 | ||
249 | pub fn load_out_dirs( | 254 | #[derive(Debug, Clone, Default)] |
250 | cargo_toml: &Path, | 255 | pub struct ExternResources { |
251 | cargo_features: &CargoFeatures, | 256 | out_dirs: FxHashMap<PackageId, PathBuf>, |
252 | ) -> FxHashMap<PackageId, PathBuf> { | 257 | proc_dylib_paths: FxHashMap<PackageId, PathBuf>, |
258 | } | ||
259 | |||
260 | pub fn load_extern_resources(cargo_toml: &Path, cargo_features: &CargoFeatures) -> ExternResources { | ||
253 | let mut args: Vec<String> = vec![ | 261 | let mut args: Vec<String> = vec![ |
254 | "check".to_string(), | 262 | "check".to_string(), |
255 | "--message-format=json".to_string(), | 263 | "--message-format=json".to_string(), |
@@ -267,14 +275,21 @@ pub fn load_out_dirs( | |||
267 | args.extend(cargo_features.features.iter().cloned()); | 275 | args.extend(cargo_features.features.iter().cloned()); |
268 | } | 276 | } |
269 | 277 | ||
270 | let mut acc = FxHashMap::default(); | 278 | let mut acc = ExternResources::default(); |
271 | let res = run_cargo(&args, cargo_toml.parent(), &mut |message| { | 279 | let res = run_cargo(&args, cargo_toml.parent(), &mut |message| { |
272 | match message { | 280 | match message { |
273 | Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => { | 281 | Message::BuildScriptExecuted(BuildScript { package_id, out_dir, .. }) => { |
274 | acc.insert(package_id, out_dir); | 282 | acc.out_dirs.insert(package_id, out_dir); |
275 | } | 283 | } |
276 | 284 | ||
277 | Message::CompilerArtifact(_) => (), | 285 | Message::CompilerArtifact(message) => { |
286 | if message.target.kind.contains(&"proc-macro".to_string()) { | ||
287 | let package_id = message.package_id; | ||
288 | if let Some(filename) = message.filenames.get(0) { | ||
289 | acc.proc_dylib_paths.insert(package_id, filename.clone()); | ||
290 | } | ||
291 | } | ||
292 | } | ||
278 | Message::CompilerMessage(_) => (), | 293 | Message::CompilerMessage(_) => (), |
279 | Message::Unknown => (), | 294 | Message::Unknown => (), |
280 | } | 295 | } |
diff --git a/crates/ra_project_model/src/json_project.rs b/crates/ra_project_model/src/json_project.rs index 336446e58..b030c8a6a 100644 --- a/crates/ra_project_model/src/json_project.rs +++ b/crates/ra_project_model/src/json_project.rs | |||
@@ -23,6 +23,7 @@ pub struct Crate { | |||
23 | pub(crate) atom_cfgs: FxHashSet<String>, | 23 | pub(crate) atom_cfgs: FxHashSet<String>, |
24 | pub(crate) key_value_cfgs: FxHashMap<String, String>, | 24 | pub(crate) key_value_cfgs: FxHashMap<String, String>, |
25 | pub(crate) out_dir: Option<PathBuf>, | 25 | pub(crate) out_dir: Option<PathBuf>, |
26 | pub(crate) proc_macro_dylib_path: Option<PathBuf>, | ||
26 | } | 27 | } |
27 | 28 | ||
28 | #[derive(Clone, Copy, Debug, Deserialize)] | 29 | #[derive(Clone, Copy, Debug, Deserialize)] |
diff --git a/crates/ra_project_model/src/lib.rs b/crates/ra_project_model/src/lib.rs index a3ef9acdc..444d3bb3f 100644 --- a/crates/ra_project_model/src/lib.rs +++ b/crates/ra_project_model/src/lib.rs | |||
@@ -23,6 +23,7 @@ pub use crate::{ | |||
23 | json_project::JsonProject, | 23 | json_project::JsonProject, |
24 | sysroot::Sysroot, | 24 | sysroot::Sysroot, |
25 | }; | 25 | }; |
26 | pub use ra_proc_macro::ProcMacroClient; | ||
26 | 27 | ||
27 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] | 28 | #[derive(Clone, PartialEq, Eq, Hash, Debug)] |
28 | pub struct CargoTomlNotFoundError { | 29 | pub struct CargoTomlNotFoundError { |
@@ -173,6 +174,29 @@ impl ProjectWorkspace { | |||
173 | } | 174 | } |
174 | } | 175 | } |
175 | 176 | ||
177 | pub fn proc_macro_dylib_paths(&self) -> Vec<PathBuf> { | ||
178 | match self { | ||
179 | ProjectWorkspace::Json { project } => { | ||
180 | let mut proc_macro_dylib_paths = Vec::with_capacity(project.crates.len()); | ||
181 | for krate in &project.crates { | ||
182 | if let Some(out_dir) = &krate.proc_macro_dylib_path { | ||
183 | proc_macro_dylib_paths.push(out_dir.to_path_buf()); | ||
184 | } | ||
185 | } | ||
186 | proc_macro_dylib_paths | ||
187 | } | ||
188 | ProjectWorkspace::Cargo { cargo, sysroot: _sysroot } => { | ||
189 | let mut proc_macro_dylib_paths = Vec::with_capacity(cargo.packages().len()); | ||
190 | for pkg in cargo.packages() { | ||
191 | if let Some(dylib_path) = &cargo[pkg].proc_macro_dylib_path { | ||
192 | proc_macro_dylib_paths.push(dylib_path.to_path_buf()); | ||
193 | } | ||
194 | } | ||
195 | proc_macro_dylib_paths | ||
196 | } | ||
197 | } | ||
198 | } | ||
199 | |||
176 | pub fn n_packages(&self) -> usize { | 200 | pub fn n_packages(&self) -> usize { |
177 | match self { | 201 | match self { |
178 | ProjectWorkspace::Json { project } => project.crates.len(), | 202 | ProjectWorkspace::Json { project } => project.crates.len(), |
@@ -186,6 +210,7 @@ impl ProjectWorkspace { | |||
186 | &self, | 210 | &self, |
187 | default_cfg_options: &CfgOptions, | 211 | default_cfg_options: &CfgOptions, |
188 | extern_source_roots: &FxHashMap<PathBuf, ExternSourceId>, | 212 | extern_source_roots: &FxHashMap<PathBuf, ExternSourceId>, |
213 | proc_macro_client: &ProcMacroClient, | ||
189 | load: &mut dyn FnMut(&Path) -> Option<FileId>, | 214 | load: &mut dyn FnMut(&Path) -> Option<FileId>, |
190 | ) -> CrateGraph { | 215 | ) -> CrateGraph { |
191 | let mut crate_graph = CrateGraph::default(); | 216 | let mut crate_graph = CrateGraph::default(); |
@@ -219,7 +244,10 @@ impl ProjectWorkspace { | |||
219 | extern_source.set_extern_path(&out_dir, extern_source_id); | 244 | extern_source.set_extern_path(&out_dir, extern_source_id); |
220 | } | 245 | } |
221 | } | 246 | } |
222 | 247 | let proc_macro = krate | |
248 | .proc_macro_dylib_path | ||
249 | .clone() | ||
250 | .map(|it| proc_macro_client.by_dylib_path(&it)); | ||
223 | // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env | 251 | // FIXME: No crate name in json definition such that we cannot add OUT_DIR to env |
224 | crates.insert( | 252 | crates.insert( |
225 | crate_id, | 253 | crate_id, |
@@ -231,6 +259,7 @@ impl ProjectWorkspace { | |||
231 | cfg_options, | 259 | cfg_options, |
232 | env, | 260 | env, |
233 | extern_source, | 261 | extern_source, |
262 | proc_macro.unwrap_or_default(), | ||
234 | ), | 263 | ), |
235 | ); | 264 | ); |
236 | } | 265 | } |
@@ -270,6 +299,8 @@ impl ProjectWorkspace { | |||
270 | 299 | ||
271 | let env = Env::default(); | 300 | let env = Env::default(); |
272 | let extern_source = ExternSource::default(); | 301 | let extern_source = ExternSource::default(); |
302 | let proc_macro = vec![]; | ||
303 | |||
273 | let crate_id = crate_graph.add_crate_root( | 304 | let crate_id = crate_graph.add_crate_root( |
274 | file_id, | 305 | file_id, |
275 | Edition::Edition2018, | 306 | Edition::Edition2018, |
@@ -280,6 +311,7 @@ impl ProjectWorkspace { | |||
280 | cfg_options, | 311 | cfg_options, |
281 | env, | 312 | env, |
282 | extern_source, | 313 | extern_source, |
314 | proc_macro, | ||
283 | ); | 315 | ); |
284 | sysroot_crates.insert(krate, crate_id); | 316 | sysroot_crates.insert(krate, crate_id); |
285 | } | 317 | } |
@@ -327,6 +359,12 @@ impl ProjectWorkspace { | |||
327 | extern_source.set_extern_path(&out_dir, extern_source_id); | 359 | extern_source.set_extern_path(&out_dir, extern_source_id); |
328 | } | 360 | } |
329 | } | 361 | } |
362 | let proc_macro = cargo[pkg] | ||
363 | .proc_macro_dylib_path | ||
364 | .as_ref() | ||
365 | .map(|it| proc_macro_client.by_dylib_path(&it)) | ||
366 | .unwrap_or_default(); | ||
367 | |||
330 | let crate_id = crate_graph.add_crate_root( | 368 | let crate_id = crate_graph.add_crate_root( |
331 | file_id, | 369 | file_id, |
332 | edition, | 370 | edition, |
@@ -334,6 +372,7 @@ impl ProjectWorkspace { | |||
334 | cfg_options, | 372 | cfg_options, |
335 | env, | 373 | env, |
336 | extern_source, | 374 | extern_source, |
375 | proc_macro.clone(), | ||
337 | ); | 376 | ); |
338 | if cargo[tgt].kind == TargetKind::Lib { | 377 | if cargo[tgt].kind == TargetKind::Lib { |
339 | lib_tgt = Some((crate_id, cargo[tgt].name.clone())); | 378 | lib_tgt = Some((crate_id, cargo[tgt].name.clone())); |
diff --git a/crates/ra_syntax/src/ast/make.rs b/crates/ra_syntax/src/ast/make.rs index 6aee39203..69bacf224 100644 --- a/crates/ra_syntax/src/ast/make.rs +++ b/crates/ra_syntax/src/ast/make.rs | |||
@@ -258,6 +258,10 @@ pub fn token(kind: SyntaxKind) -> SyntaxToken { | |||
258 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) | 258 | .unwrap_or_else(|| panic!("unhandled token: {:?}", kind)) |
259 | } | 259 | } |
260 | 260 | ||
261 | pub fn unreachable_macro_call() -> ast::MacroCall { | ||
262 | ast_from_text(&format!("unreachable!()")) | ||
263 | } | ||
264 | |||
261 | fn ast_from_text<N: AstNode>(text: &str) -> N { | 265 | fn ast_from_text<N: AstNode>(text: &str) -> N { |
262 | let parse = SourceFile::parse(text); | 266 | let parse = SourceFile::parse(text); |
263 | let node = parse.tree().syntax().descendants().find_map(N::cast).unwrap(); | 267 | let node = parse.tree().syntax().descendants().find_map(N::cast).unwrap(); |
diff --git a/crates/ra_tt/src/lib.rs b/crates/ra_tt/src/lib.rs index 1e2fb8b91..1015ce0a6 100644 --- a/crates/ra_tt/src/lib.rs +++ b/crates/ra_tt/src/lib.rs | |||
@@ -14,9 +14,12 @@ macro_rules! impl_froms { | |||
14 | } | 14 | } |
15 | } | 15 | } |
16 | 16 | ||
17 | use std::fmt; | 17 | use std::{ |
18 | fmt::{self, Debug}, | ||
19 | panic::RefUnwindSafe, | ||
20 | }; | ||
18 | 21 | ||
19 | use smol_str::SmolStr; | 22 | pub use smol_str::SmolStr; |
20 | 23 | ||
21 | /// Represents identity of the token. | 24 | /// Represents identity of the token. |
22 | /// | 25 | /// |
@@ -184,3 +187,11 @@ impl Subtree { | |||
184 | } | 187 | } |
185 | 188 | ||
186 | pub mod buffer; | 189 | pub mod buffer; |
190 | |||
191 | #[derive(Debug, PartialEq, Eq)] | ||
192 | pub enum ExpansionError {} | ||
193 | |||
194 | pub trait TokenExpander: Debug + Send + Sync + RefUnwindSafe { | ||
195 | fn expand(&self, subtree: &Subtree, attrs: Option<&Subtree>) | ||
196 | -> Result<Subtree, ExpansionError>; | ||
197 | } | ||
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index 54e2fa1a7..832f04226 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs | |||
@@ -7,7 +7,9 @@ use anyhow::Result; | |||
7 | use crossbeam_channel::{unbounded, Receiver}; | 7 | use crossbeam_channel::{unbounded, Receiver}; |
8 | use ra_db::{ExternSourceId, FileId, SourceRootId}; | 8 | use ra_db::{ExternSourceId, FileId, SourceRootId}; |
9 | use ra_ide::{AnalysisChange, AnalysisHost}; | 9 | use ra_ide::{AnalysisChange, AnalysisHost}; |
10 | use ra_project_model::{get_rustc_cfg_options, CargoFeatures, PackageRoot, ProjectWorkspace}; | 10 | use ra_project_model::{ |
11 | get_rustc_cfg_options, CargoFeatures, PackageRoot, ProcMacroClient, ProjectWorkspace, | ||
12 | }; | ||
11 | use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; | 13 | use ra_vfs::{RootEntry, Vfs, VfsChange, VfsTask, Watch}; |
12 | use rustc_hash::{FxHashMap, FxHashSet}; | 14 | use rustc_hash::{FxHashMap, FxHashSet}; |
13 | 15 | ||
@@ -67,7 +69,9 @@ pub(crate) fn load_cargo( | |||
67 | (source_root_id, project_root) | 69 | (source_root_id, project_root) |
68 | }) | 70 | }) |
69 | .collect::<FxHashMap<_, _>>(); | 71 | .collect::<FxHashMap<_, _>>(); |
70 | let host = load(&source_roots, ws, &mut vfs, receiver, extern_dirs); | 72 | |
73 | let proc_macro_client = ProcMacroClient::dummy(); | ||
74 | let host = load(&source_roots, ws, &mut vfs, receiver, extern_dirs, &proc_macro_client); | ||
71 | Ok((host, source_roots)) | 75 | Ok((host, source_roots)) |
72 | } | 76 | } |
73 | 77 | ||
@@ -77,6 +81,7 @@ pub(crate) fn load( | |||
77 | vfs: &mut Vfs, | 81 | vfs: &mut Vfs, |
78 | receiver: Receiver<VfsTask>, | 82 | receiver: Receiver<VfsTask>, |
79 | extern_dirs: FxHashSet<PathBuf>, | 83 | extern_dirs: FxHashSet<PathBuf>, |
84 | proc_macro_client: &ProcMacroClient, | ||
80 | ) -> AnalysisHost { | 85 | ) -> AnalysisHost { |
81 | let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok()); | 86 | let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<usize>().ok()); |
82 | let mut host = AnalysisHost::new(lru_cap); | 87 | let mut host = AnalysisHost::new(lru_cap); |
@@ -143,12 +148,16 @@ pub(crate) fn load( | |||
143 | opts | 148 | opts |
144 | }; | 149 | }; |
145 | 150 | ||
146 | let crate_graph = | 151 | let crate_graph = ws.to_crate_graph( |
147 | ws.to_crate_graph(&default_cfg_options, &extern_source_roots, &mut |path: &Path| { | 152 | &default_cfg_options, |
153 | &extern_source_roots, | ||
154 | proc_macro_client, | ||
155 | &mut |path: &Path| { | ||
148 | let vfs_file = vfs.load(path); | 156 | let vfs_file = vfs.load(path); |
149 | log::debug!("vfs file {:?} -> {:?}", path, vfs_file); | 157 | log::debug!("vfs file {:?} -> {:?}", path, vfs_file); |
150 | vfs_file.map(vfs_file_to_id) | 158 | vfs_file.map(vfs_file_to_id) |
151 | }); | 159 | }, |
160 | ); | ||
152 | log::debug!("crate graph: {:?}", crate_graph); | 161 | log::debug!("crate graph: {:?}", crate_graph); |
153 | analysis_change.set_crate_graph(crate_graph); | 162 | analysis_change.set_crate_graph(crate_graph); |
154 | 163 | ||
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs index c4244fee2..de85bb017 100644 --- a/crates/rust-analyzer/src/world.rs +++ b/crates/rust-analyzer/src/world.rs | |||
@@ -16,7 +16,7 @@ use ra_ide::{ | |||
16 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsOptions, LibraryData, | 16 | Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, InlayHintsOptions, LibraryData, |
17 | SourceRootId, | 17 | SourceRootId, |
18 | }; | 18 | }; |
19 | use ra_project_model::{get_rustc_cfg_options, ProjectWorkspace}; | 19 | use ra_project_model::{get_rustc_cfg_options, ProcMacroClient, ProjectWorkspace}; |
20 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; | 20 | use ra_vfs::{LineEndings, RootEntry, Vfs, VfsChange, VfsFile, VfsRoot, VfsTask, Watch}; |
21 | use relative_path::RelativePathBuf; | 21 | use relative_path::RelativePathBuf; |
22 | 22 | ||
@@ -150,9 +150,19 @@ impl WorldState { | |||
150 | vfs_file.map(|f| FileId(f.0)) | 150 | vfs_file.map(|f| FileId(f.0)) |
151 | }; | 151 | }; |
152 | 152 | ||
153 | let proc_macro_client = | ||
154 | ProcMacroClient::extern_process(std::path::Path::new("ra_proc_macro_srv")); | ||
155 | |||
153 | workspaces | 156 | workspaces |
154 | .iter() | 157 | .iter() |
155 | .map(|ws| ws.to_crate_graph(&default_cfg_options, &extern_source_roots, &mut load)) | 158 | .map(|ws| { |
159 | ws.to_crate_graph( | ||
160 | &default_cfg_options, | ||
161 | &extern_source_roots, | ||
162 | &proc_macro_client, | ||
163 | &mut load, | ||
164 | ) | ||
165 | }) | ||
156 | .for_each(|graph| { | 166 | .for_each(|graph| { |
157 | crate_graph.extend(graph); | 167 | crate_graph.extend(graph); |
158 | }); | 168 | }); |
diff --git a/docs/user/assists.md b/docs/user/assists.md index f3ce6b0e0..b2568a954 100644 --- a/docs/user/assists.md +++ b/docs/user/assists.md | |||
@@ -597,6 +597,29 @@ use std::collections::HashMap; | |||
597 | fn process(map: HashMap<String, String>) {} | 597 | fn process(map: HashMap<String, String>) {} |
598 | ``` | 598 | ``` |
599 | 599 | ||
600 | ## `replace_unwrap_with_match` | ||
601 | |||
602 | Replaces `unwrap` a `match` expression. Works for Result and Option. | ||
603 | |||
604 | ```rust | ||
605 | // BEFORE | ||
606 | enum Result<T, E> { Ok(T), Err(E) } | ||
607 | fn main() { | ||
608 | let x: Result<i32, i32> = Result::Ok(92); | ||
609 | let y = x.┃unwrap(); | ||
610 | } | ||
611 | |||
612 | // AFTER | ||
613 | enum Result<T, E> { Ok(T), Err(E) } | ||
614 | fn main() { | ||
615 | let x: Result<i32, i32> = Result::Ok(92); | ||
616 | let y = match x { | ||
617 | Ok(a) => a, | ||
618 | _ => unreachable!(), | ||
619 | }; | ||
620 | } | ||
621 | ``` | ||
622 | |||
600 | ## `split_import` | 623 | ## `split_import` |
601 | 624 | ||
602 | Wraps the tail of import into braces. | 625 | Wraps the tail of import into braces. |
diff --git a/editors/code/src/commands/server_version.ts b/editors/code/src/commands/server_version.ts index 03528b825..d64ac726e 100644 --- a/editors/code/src/commands/server_version.ts +++ b/editors/code/src/commands/server_version.ts | |||
@@ -4,7 +4,12 @@ import { Ctx, Cmd } from '../ctx'; | |||
4 | 4 | ||
5 | export function serverVersion(ctx: Ctx): Cmd { | 5 | export function serverVersion(ctx: Ctx): Cmd { |
6 | return async () => { | 6 | return async () => { |
7 | const version = spawnSync(ctx.serverPath, ["--version"], { encoding: "utf8" }).stdout; | 7 | const { stdout } = spawnSync(ctx.serverPath, ["--version"], { encoding: "utf8" }); |
8 | vscode.window.showInformationMessage('rust-analyzer version : ' + version); | 8 | const commitHash = stdout.slice(`rust-analyzer `.length).trim(); |
9 | const { releaseTag } = ctx.config.package; | ||
10 | |||
11 | void vscode.window.showInformationMessage( | ||
12 | `rust-analyzer version: ${releaseTag ?? "unreleased"} (${commitHash})` | ||
13 | ); | ||
9 | }; | 14 | }; |
10 | } | 15 | } |
diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 637aea27d..e77462c1b 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts | |||
@@ -21,7 +21,7 @@ export class Config { | |||
21 | 21 | ||
22 | readonly package: { | 22 | readonly package: { |
23 | version: string; | 23 | version: string; |
24 | releaseTag: string | undefined; | 24 | releaseTag: string | null; |
25 | enableProposedApi: boolean | undefined; | 25 | enableProposedApi: boolean | undefined; |
26 | } = vscode.extensions.getExtension(this.extensionId)!.packageJSON; | 26 | } = vscode.extensions.getExtension(this.extensionId)!.packageJSON; |
27 | 27 | ||
diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index de27d9535..980ed925b 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts | |||
@@ -48,21 +48,19 @@ export async function activate(context: vscode.ExtensionContext) { | |||
48 | ctx = await Ctx.create(config, context, serverPath); | 48 | ctx = await Ctx.create(config, context, serverPath); |
49 | 49 | ||
50 | // Commands which invokes manually via command palette, shortcut, etc. | 50 | // Commands which invokes manually via command palette, shortcut, etc. |
51 | ctx.registerCommand('reload', (ctx) => { | 51 | |
52 | return async () => { | 52 | // Reloading is inspired by @DanTup maneuver: https://github.com/microsoft/vscode/issues/45774#issuecomment-373423895 |
53 | vscode.window.showInformationMessage('Reloading rust-analyzer...'); | 53 | ctx.registerCommand('reload', _ => async () => { |
54 | // @DanTup maneuver | 54 | void vscode.window.showInformationMessage('Reloading rust-analyzer...'); |
55 | // https://github.com/microsoft/vscode/issues/45774#issuecomment-373423895 | 55 | await deactivate(); |
56 | await deactivate(); | 56 | while (context.subscriptions.length > 0) { |
57 | for (const sub of ctx.subscriptions) { | 57 | try { |
58 | try { | 58 | context.subscriptions.pop()!.dispose(); |
59 | sub.dispose(); | 59 | } catch (err) { |
60 | } catch (e) { | 60 | log.error("Dispose error:", err); |
61 | log.error(e); | ||
62 | } | ||
63 | } | 61 | } |
64 | await activate(context); | 62 | } |
65 | }; | 63 | await activate(context).catch(log.error); |
66 | }); | 64 | }); |
67 | 65 | ||
68 | ctx.registerCommand('analyzerStatus', commands.analyzerStatus); | 66 | ctx.registerCommand('analyzerStatus', commands.analyzerStatus); |
@@ -96,7 +94,7 @@ export async function activate(context: vscode.ExtensionContext) { | |||
96 | } | 94 | } |
97 | 95 | ||
98 | export async function deactivate() { | 96 | export async function deactivate() { |
99 | await ctx?.client?.stop(); | 97 | await ctx?.client.stop(); |
100 | ctx = undefined; | 98 | ctx = undefined; |
101 | } | 99 | } |
102 | 100 | ||
@@ -110,11 +108,13 @@ async function bootstrap(config: Config, state: PersistentState): Promise<string | |||
110 | } | 108 | } |
111 | 109 | ||
112 | async function bootstrapExtension(config: Config, state: PersistentState): Promise<void> { | 110 | async function bootstrapExtension(config: Config, state: PersistentState): Promise<void> { |
113 | if (config.package.releaseTag === undefined) return; | 111 | if (config.package.releaseTag === null) return; |
114 | if (config.channel === "stable") { | 112 | if (config.channel === "stable") { |
115 | if (config.package.releaseTag === NIGHTLY_TAG) { | 113 | if (config.package.releaseTag === NIGHTLY_TAG) { |
116 | vscode.window.showWarningMessage(`You are running a nightly version of rust-analyzer extension. | 114 | void vscode.window.showWarningMessage( |
117 | To switch to stable, uninstall the extension and re-install it from the marketplace`); | 115 | `You are running a nightly version of rust-analyzer extension. ` + |
116 | `To switch to stable, uninstall the extension and re-install it from the marketplace` | ||
117 | ); | ||
118 | } | 118 | } |
119 | return; | 119 | return; |
120 | }; | 120 | }; |
@@ -169,9 +169,7 @@ async function bootstrapServer(config: Config, state: PersistentState): Promise< | |||
169 | log.debug("Checked binary availability via --version", res); | 169 | log.debug("Checked binary availability via --version", res); |
170 | log.debug(res, "--version output:", res.output); | 170 | log.debug(res, "--version output:", res.output); |
171 | if (res.status !== 0) { | 171 | if (res.status !== 0) { |
172 | throw new Error( | 172 | throw new Error(`Failed to execute ${path} --version`); |
173 | `Failed to execute ${path} --version` | ||
174 | ); | ||
175 | } | 173 | } |
176 | 174 | ||
177 | return path; | 175 | return path; |
@@ -185,7 +183,7 @@ async function getServer(config: Config, state: PersistentState): Promise<string | |||
185 | } | 183 | } |
186 | return explicitPath; | 184 | return explicitPath; |
187 | }; | 185 | }; |
188 | if (config.package.releaseTag === undefined) return "rust-analyzer"; | 186 | if (config.package.releaseTag === null) return "rust-analyzer"; |
189 | 187 | ||
190 | let binaryName: string | undefined = undefined; | 188 | let binaryName: string | undefined = undefined; |
191 | if (process.arch === "x64" || process.arch === "ia32") { | 189 | if (process.arch === "x64" || process.arch === "ia32") { |