aboutsummaryrefslogtreecommitdiff
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/ra_hir_def/src/body.rs6
-rw-r--r--crates/ra_hir_def/src/body/lower.rs6
-rw-r--r--crates/ra_hir_def/src/diagnostics.rs2
-rw-r--r--crates/ra_hir_expand/src/ast_id_map.rs2
-rw-r--r--crates/ra_hir_ty/src/diagnostics.rs10
-rw-r--r--crates/ra_hir_ty/src/expr.rs6
-rw-r--r--crates/ra_hir_ty/src/tests.rs2
-rw-r--r--crates/ra_ide/src/inlay_hints.rs17
-rw-r--r--crates/ra_proc_macro_srv/Cargo.toml5
-rw-r--r--crates/ra_proc_macro_srv/src/dylib.rs211
-rw-r--r--crates/ra_proc_macro_srv/src/lib.rs36
-rw-r--r--crates/ra_proc_macro_srv/src/rustc_server.rs4
-rw-r--r--crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt188
-rw-r--r--crates/ra_proc_macro_srv/src/tests/mod.rs47
-rw-r--r--crates/ra_proc_macro_srv/src/tests/utils.rs65
-rw-r--r--crates/ra_syntax/src/ptr.rs17
-rw-r--r--crates/rust-analyzer/src/cli/load_cargo.rs4
-rw-r--r--crates/rust-analyzer/src/world.rs4
18 files changed, 594 insertions, 38 deletions
diff --git a/crates/ra_hir_def/src/body.rs b/crates/ra_hir_def/src/body.rs
index 5f9d53ecb..e09996c6f 100644
--- a/crates/ra_hir_def/src/body.rs
+++ b/crates/ra_hir_def/src/body.rs
@@ -236,7 +236,7 @@ impl Index<PatId> for Body {
236 236
237impl BodySourceMap { 237impl BodySourceMap {
238 pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> { 238 pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> {
239 self.expr_map_back[expr] 239 self.expr_map_back[expr].clone()
240 } 240 }
241 241
242 pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> { 242 pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> {
@@ -255,7 +255,7 @@ impl BodySourceMap {
255 } 255 }
256 256
257 pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> { 257 pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> {
258 self.pat_map_back[pat] 258 self.pat_map_back[pat].clone()
259 } 259 }
260 260
261 pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> { 261 pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> {
@@ -264,6 +264,6 @@ impl BodySourceMap {
264 } 264 }
265 265
266 pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr<ast::RecordField> { 266 pub fn field_syntax(&self, expr: ExprId, field: usize) -> AstPtr<ast::RecordField> {
267 self.field_map[&(expr, field)] 267 self.field_map[&(expr, field)].clone()
268 } 268 }
269} 269}
diff --git a/crates/ra_hir_def/src/body/lower.rs b/crates/ra_hir_def/src/body/lower.rs
index 0855c1d3a..9d6ee095e 100644
--- a/crates/ra_hir_def/src/body/lower.rs
+++ b/crates/ra_hir_def/src/body/lower.rs
@@ -106,7 +106,7 @@ impl ExprCollector<'_> {
106 fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr<ast::Expr>) -> ExprId { 106 fn alloc_expr(&mut self, expr: Expr, ptr: AstPtr<ast::Expr>) -> ExprId {
107 let ptr = Either::Left(ptr); 107 let ptr = Either::Left(ptr);
108 let src = self.expander.to_source(ptr); 108 let src = self.expander.to_source(ptr);
109 let id = self.make_expr(expr, Ok(src)); 109 let id = self.make_expr(expr, Ok(src.clone()));
110 self.source_map.expr_map.insert(src, id); 110 self.source_map.expr_map.insert(src, id);
111 id 111 id
112 } 112 }
@@ -118,7 +118,7 @@ impl ExprCollector<'_> {
118 fn alloc_expr_field_shorthand(&mut self, expr: Expr, ptr: AstPtr<ast::RecordField>) -> ExprId { 118 fn alloc_expr_field_shorthand(&mut self, expr: Expr, ptr: AstPtr<ast::RecordField>) -> ExprId {
119 let ptr = Either::Right(ptr); 119 let ptr = Either::Right(ptr);
120 let src = self.expander.to_source(ptr); 120 let src = self.expander.to_source(ptr);
121 let id = self.make_expr(expr, Ok(src)); 121 let id = self.make_expr(expr, Ok(src.clone()));
122 self.source_map.expr_map.insert(src, id); 122 self.source_map.expr_map.insert(src, id);
123 id 123 id
124 } 124 }
@@ -136,7 +136,7 @@ impl ExprCollector<'_> {
136 136
137 fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId { 137 fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
138 let src = self.expander.to_source(ptr); 138 let src = self.expander.to_source(ptr);
139 let id = self.make_pat(pat, Ok(src)); 139 let id = self.make_pat(pat, Ok(src.clone()));
140 self.source_map.pat_map.insert(src, id); 140 self.source_map.pat_map.insert(src, id);
141 id 141 id
142 } 142 }
diff --git a/crates/ra_hir_def/src/diagnostics.rs b/crates/ra_hir_def/src/diagnostics.rs
index 095498429..cfa0f2f76 100644
--- a/crates/ra_hir_def/src/diagnostics.rs
+++ b/crates/ra_hir_def/src/diagnostics.rs
@@ -20,7 +20,7 @@ impl Diagnostic for UnresolvedModule {
20 "unresolved module".to_string() 20 "unresolved module".to_string()
21 } 21 }
22 fn source(&self) -> InFile<SyntaxNodePtr> { 22 fn source(&self) -> InFile<SyntaxNodePtr> {
23 InFile { file_id: self.file, value: self.decl.into() } 23 InFile { file_id: self.file, value: self.decl.clone().into() }
24 } 24 }
25 fn as_any(&self) -> &(dyn Any + Send + 'static) { 25 fn as_any(&self) -> &(dyn Any + Send + 'static) {
26 self 26 self
diff --git a/crates/ra_hir_expand/src/ast_id_map.rs b/crates/ra_hir_expand/src/ast_id_map.rs
index 5643ecdce..a3ca302c2 100644
--- a/crates/ra_hir_expand/src/ast_id_map.rs
+++ b/crates/ra_hir_expand/src/ast_id_map.rs
@@ -90,7 +90,7 @@ impl AstIdMap {
90 } 90 }
91 91
92 pub(crate) fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> { 92 pub(crate) fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
93 self.arena[id.raw].cast::<N>().unwrap() 93 self.arena[id.raw].clone().cast::<N>().unwrap()
94 } 94 }
95 95
96 fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId { 96 fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId {
diff --git a/crates/ra_hir_ty/src/diagnostics.rs b/crates/ra_hir_ty/src/diagnostics.rs
index 3f18acf1d..927896d6f 100644
--- a/crates/ra_hir_ty/src/diagnostics.rs
+++ b/crates/ra_hir_ty/src/diagnostics.rs
@@ -21,7 +21,7 @@ impl Diagnostic for NoSuchField {
21 } 21 }
22 22
23 fn source(&self) -> InFile<SyntaxNodePtr> { 23 fn source(&self) -> InFile<SyntaxNodePtr> {
24 InFile { file_id: self.file, value: self.field.into() } 24 InFile { file_id: self.file, value: self.field.clone().into() }
25 } 25 }
26 26
27 fn as_any(&self) -> &(dyn Any + Send + 'static) { 27 fn as_any(&self) -> &(dyn Any + Send + 'static) {
@@ -45,7 +45,7 @@ impl Diagnostic for MissingFields {
45 buf 45 buf
46 } 46 }
47 fn source(&self) -> InFile<SyntaxNodePtr> { 47 fn source(&self) -> InFile<SyntaxNodePtr> {
48 InFile { file_id: self.file, value: self.field_list.into() } 48 InFile { file_id: self.file, value: self.field_list.clone().into() }
49 } 49 }
50 fn as_any(&self) -> &(dyn Any + Send + 'static) { 50 fn as_any(&self) -> &(dyn Any + Send + 'static) {
51 self 51 self
@@ -78,7 +78,7 @@ impl Diagnostic for MissingPatFields {
78 buf 78 buf
79 } 79 }
80 fn source(&self) -> InFile<SyntaxNodePtr> { 80 fn source(&self) -> InFile<SyntaxNodePtr> {
81 InFile { file_id: self.file, value: self.field_list.into() } 81 InFile { file_id: self.file, value: self.field_list.clone().into() }
82 } 82 }
83 fn as_any(&self) -> &(dyn Any + Send + 'static) { 83 fn as_any(&self) -> &(dyn Any + Send + 'static) {
84 self 84 self
@@ -97,7 +97,7 @@ impl Diagnostic for MissingMatchArms {
97 String::from("Missing match arm") 97 String::from("Missing match arm")
98 } 98 }
99 fn source(&self) -> InFile<SyntaxNodePtr> { 99 fn source(&self) -> InFile<SyntaxNodePtr> {
100 InFile { file_id: self.file, value: self.match_expr.into() } 100 InFile { file_id: self.file, value: self.match_expr.clone().into() }
101 } 101 }
102 fn as_any(&self) -> &(dyn Any + Send + 'static) { 102 fn as_any(&self) -> &(dyn Any + Send + 'static) {
103 self 103 self
@@ -115,7 +115,7 @@ impl Diagnostic for MissingOkInTailExpr {
115 "wrap return expression in Ok".to_string() 115 "wrap return expression in Ok".to_string()
116 } 116 }
117 fn source(&self) -> InFile<SyntaxNodePtr> { 117 fn source(&self) -> InFile<SyntaxNodePtr> {
118 InFile { file_id: self.file, value: self.expr.into() } 118 InFile { file_id: self.file, value: self.expr.clone().into() }
119 } 119 }
120 fn as_any(&self) -> &(dyn Any + Send + 'static) { 120 fn as_any(&self) -> &(dyn Any + Send + 'static) {
121 self 121 self
diff --git a/crates/ra_hir_ty/src/expr.rs b/crates/ra_hir_ty/src/expr.rs
index a7c8d74ab..827b687de 100644
--- a/crates/ra_hir_ty/src/expr.rs
+++ b/crates/ra_hir_ty/src/expr.rs
@@ -89,7 +89,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
89 let (_, source_map) = db.body_with_source_map(self.func.into()); 89 let (_, source_map) = db.body_with_source_map(self.func.into());
90 90
91 if let Ok(source_ptr) = source_map.expr_syntax(id) { 91 if let Ok(source_ptr) = source_map.expr_syntax(id) {
92 if let Some(expr) = source_ptr.value.left() { 92 if let Some(expr) = source_ptr.value.as_ref().left() {
93 let root = source_ptr.file_syntax(db.upcast()); 93 let root = source_ptr.file_syntax(db.upcast());
94 if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) { 94 if let ast::Expr::RecordLit(record_lit) = expr.to_node(&root) {
95 if let Some(field_list) = record_lit.record_field_list() { 95 if let Some(field_list) = record_lit.record_field_list() {
@@ -120,7 +120,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
120 let (_, source_map) = db.body_with_source_map(self.func.into()); 120 let (_, source_map) = db.body_with_source_map(self.func.into());
121 121
122 if let Ok(source_ptr) = source_map.pat_syntax(id) { 122 if let Ok(source_ptr) = source_map.pat_syntax(id) {
123 if let Some(expr) = source_ptr.value.left() { 123 if let Some(expr) = source_ptr.value.as_ref().left() {
124 let root = source_ptr.file_syntax(db.upcast()); 124 let root = source_ptr.file_syntax(db.upcast());
125 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) { 125 if let ast::Pat::RecordPat(record_pat) = expr.to_node(&root) {
126 if let Some(field_list) = record_pat.record_field_pat_list() { 126 if let Some(field_list) = record_pat.record_field_pat_list() {
@@ -205,7 +205,7 @@ impl<'a, 'b> ExprValidator<'a, 'b> {
205 } 205 }
206 206
207 if let Ok(source_ptr) = source_map.expr_syntax(id) { 207 if let Ok(source_ptr) = source_map.expr_syntax(id) {
208 if let Some(expr) = source_ptr.value.left() { 208 if let Some(expr) = source_ptr.value.as_ref().left() {
209 let root = source_ptr.file_syntax(db.upcast()); 209 let root = source_ptr.file_syntax(db.upcast());
210 if let ast::Expr::MatchExpr(match_expr) = expr.to_node(&root) { 210 if let ast::Expr::MatchExpr(match_expr) = expr.to_node(&root) {
211 if let (Some(match_expr), Some(arms)) = 211 if let (Some(match_expr), Some(arms)) =
diff --git a/crates/ra_hir_ty/src/tests.rs b/crates/ra_hir_ty/src/tests.rs
index 47a7b9ffd..54e31602f 100644
--- a/crates/ra_hir_ty/src/tests.rs
+++ b/crates/ra_hir_ty/src/tests.rs
@@ -87,7 +87,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
87 } 87 }
88 Err(SyntheticSyntax) => continue, 88 Err(SyntheticSyntax) => continue,
89 }; 89 };
90 types.push((syntax_ptr, ty)); 90 types.push((syntax_ptr.clone(), ty));
91 if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) { 91 if let Some(mismatch) = inference_result.type_mismatch_for_expr(expr) {
92 mismatches.push((syntax_ptr, mismatch)); 92 mismatches.push((syntax_ptr, mismatch));
93 } 93 }
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index da9f55a69..45b9f7802 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -235,7 +235,10 @@ fn should_show_param_hint(
235 param_name: &str, 235 param_name: &str,
236 argument: &ast::Expr, 236 argument: &ast::Expr,
237) -> bool { 237) -> bool {
238 if param_name.is_empty() || is_argument_similar_to_param(argument, param_name) { 238 if param_name.is_empty()
239 || is_argument_similar_to_param(argument, param_name)
240 || Some(param_name) == fn_signature.name.as_ref().map(String::as_str)
241 {
239 return false; 242 return false;
240 } 243 }
241 244
@@ -247,10 +250,7 @@ fn should_show_param_hint(
247 250
248 // avoid displaying hints for common functions like map, filter, etc. 251 // avoid displaying hints for common functions like map, filter, etc.
249 // or other obvious words used in std 252 // or other obvious words used in std
250 if parameters_len == 1 && is_obvious_param(param_name) { 253 parameters_len != 1 || !is_obvious_param(param_name)
251 return false;
252 }
253 true
254} 254}
255 255
256fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool { 256fn is_argument_similar_to_param(argument: &ast::Expr, param_name: &str) -> bool {
@@ -1086,6 +1086,8 @@ impl Test {
1086 } 1086 }
1087 1087
1088 fn no_hints_expected(&self, _: i32, test_var: i32) {} 1088 fn no_hints_expected(&self, _: i32, test_var: i32) {}
1089
1090 fn frob(&self, frob: bool) {}
1089} 1091}
1090 1092
1091struct Param {} 1093struct Param {}
@@ -1093,6 +1095,8 @@ struct Param {}
1093fn different_order(param: &Param) {} 1095fn different_order(param: &Param) {}
1094fn different_order_mut(param: &mut Param) {} 1096fn different_order_mut(param: &mut Param) {}
1095 1097
1098fn twiddle(twiddle: bool) {}
1099
1096fn main() { 1100fn main() {
1097 let container: TestVarContainer = TestVarContainer { test_var: 42 }; 1101 let container: TestVarContainer = TestVarContainer { test_var: 42 };
1098 let test: Test = Test {}; 1102 let test: Test = Test {};
@@ -1105,6 +1109,9 @@ fn main() {
1105 let test_var: i32 = 55; 1109 let test_var: i32 = 55;
1106 test_processed.no_hints_expected(22, test_var); 1110 test_processed.no_hints_expected(22, test_var);
1107 test_processed.no_hints_expected(33, container.test_var); 1111 test_processed.no_hints_expected(33, container.test_var);
1112 test_processed.frob(false);
1113
1114 twiddle(true);
1108 1115
1109 let param_begin: Param = Param {}; 1116 let param_begin: Param = Param {};
1110 different_order(&param_begin); 1117 different_order(&param_begin);
diff --git a/crates/ra_proc_macro_srv/Cargo.toml b/crates/ra_proc_macro_srv/Cargo.toml
index f08de5fc7..1e0f50339 100644
--- a/crates/ra_proc_macro_srv/Cargo.toml
+++ b/crates/ra_proc_macro_srv/Cargo.toml
@@ -12,9 +12,12 @@ doctest = false
12ra_tt = { path = "../ra_tt" } 12ra_tt = { path = "../ra_tt" }
13ra_mbe = { path = "../ra_mbe" } 13ra_mbe = { path = "../ra_mbe" }
14ra_proc_macro = { path = "../ra_proc_macro" } 14ra_proc_macro = { path = "../ra_proc_macro" }
15goblin = "0.2.1"
16libloading = "0.6.0"
17test_utils = { path = "../test_utils" }
15 18
16[dev-dependencies] 19[dev-dependencies]
17cargo_metadata = "0.9.1" 20cargo_metadata = "0.9.1"
18difference = "2.0.0" 21difference = "2.0.0"
19# used as proc macro test target 22# used as proc macro test target
20serde_derive = "=1.0.104" \ No newline at end of file 23serde_derive = "=1.0.104"
diff --git a/crates/ra_proc_macro_srv/src/dylib.rs b/crates/ra_proc_macro_srv/src/dylib.rs
new file mode 100644
index 000000000..ec63d587b
--- /dev/null
+++ b/crates/ra_proc_macro_srv/src/dylib.rs
@@ -0,0 +1,211 @@
1//! Handles dynamic library loading for proc macro
2
3use crate::{proc_macro::bridge, rustc_server::TokenStream};
4use std::path::Path;
5
6use goblin::{mach::Mach, Object};
7use libloading::Library;
8use ra_proc_macro::ProcMacroKind;
9
10use std::io::Error as IoError;
11use std::io::ErrorKind as IoErrorKind;
12
13const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";
14
15fn invalid_data_err(e: impl Into<Box<dyn std::error::Error + Send + Sync>>) -> IoError {
16 IoError::new(IoErrorKind::InvalidData, e)
17}
18
19fn get_symbols_from_lib(file: &Path) -> Result<Vec<String>, IoError> {
20 let buffer = std::fs::read(file)?;
21 let object = Object::parse(&buffer).map_err(invalid_data_err)?;
22
23 match object {
24 Object::Elf(elf) => {
25 let symbols = elf.dynstrtab.to_vec().map_err(invalid_data_err)?;
26 let names = symbols.iter().map(|s| s.to_string()).collect();
27 Ok(names)
28 }
29 Object::PE(pe) => {
30 let symbol_names =
31 pe.exports.iter().flat_map(|s| s.name).map(|n| n.to_string()).collect();
32 Ok(symbol_names)
33 }
34 Object::Mach(mach) => match mach {
35 Mach::Binary(binary) => {
36 let exports = binary.exports().map_err(invalid_data_err)?;
37 let names = exports
38 .into_iter()
39 .map(|s| {
40 // In macos doc:
41 // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlsym.3.html
42 // Unlike other dyld API's, the symbol name passed to dlsym() must NOT be
43 // prepended with an underscore.
44 if s.name.starts_with("_") {
45 s.name[1..].to_string()
46 } else {
47 s.name
48 }
49 })
50 .collect();
51 Ok(names)
52 }
53 Mach::Fat(_) => Ok(vec![]),
54 },
55 Object::Archive(_) | Object::Unknown(_) => Ok(vec![]),
56 }
57}
58
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.
69///
70/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
71/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample)
72/// and [here](https://github.com/rust-lang/rust/issues/60593).
73///
74/// Usage of RTLD_DEEPBIND
75/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1)
76///
77/// It seems that on Windows that behaviour is default, so we do nothing in that case.
78#[cfg(windows)]
79fn load_library(file: &Path) -> Result<Library, libloading::Error> {
80 Library::new(file)
81}
82
83#[cfg(unix)]
84fn load_library(file: &Path) -> Result<Library, libloading::Error> {
85 use libloading::os::unix::Library as UnixLibrary;
86 use std::os::raw::c_int;
87
88 const RTLD_NOW: c_int = 0x00002;
89 const RTLD_DEEPBIND: c_int = 0x00008;
90
91 UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into())
92}
93
94struct ProcMacroLibraryLibloading {
95 // Hold the dylib to prevent it for unloadeding
96 _lib: Library,
97 exported_macros: Vec<bridge::client::ProcMacro>,
98}
99
100impl ProcMacroLibraryLibloading {
101 fn open(file: &Path) -> Result<Self, IoError> {
102 let symbol_name = find_registrar_symbol(file)?
103 .ok_or(invalid_data_err(format!("Cannot find registrar symbol in file {:?}", file)))?;
104
105 let lib = load_library(file).map_err(invalid_data_err)?;
106 let exported_macros = {
107 let macros: libloading::Symbol<&&[bridge::client::ProcMacro]> =
108 unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?;
109 macros.to_vec()
110 };
111
112 Ok(ProcMacroLibraryLibloading { _lib: lib, exported_macros })
113 }
114}
115
116type ProcMacroLibraryImpl = ProcMacroLibraryLibloading;
117
118pub struct Expander {
119 libs: Vec<ProcMacroLibraryImpl>,
120}
121
122impl Expander {
123 pub fn new<P: AsRef<Path>>(lib: &P) -> Result<Expander, String> {
124 let mut libs = vec![];
125 /* Some libraries for dynamic loading require canonicalized path (even when it is
126 already absolute
127 */
128 let lib =
129 lib.as_ref().canonicalize().expect(&format!("Cannot canonicalize {:?}", lib.as_ref()));
130
131 let library = ProcMacroLibraryImpl::open(&lib).map_err(|e| e.to_string())?;
132 libs.push(library);
133
134 Ok(Expander { libs })
135 }
136
137 pub fn expand(
138 &self,
139 macro_name: &str,
140 macro_body: &ra_tt::Subtree,
141 attributes: Option<&ra_tt::Subtree>,
142 ) -> Result<ra_tt::Subtree, bridge::PanicMessage> {
143 let parsed_body = TokenStream::with_subtree(macro_body.clone());
144
145 let parsed_attributes = attributes
146 .map_or(crate::rustc_server::TokenStream::new(), |attr| {
147 TokenStream::with_subtree(attr.clone())
148 });
149
150 for lib in &self.libs {
151 for proc_macro in &lib.exported_macros {
152 match proc_macro {
153 bridge::client::ProcMacro::CustomDerive { trait_name, client, .. }
154 if *trait_name == macro_name =>
155 {
156 let res = client.run(
157 &crate::proc_macro::bridge::server::SameThread,
158 crate::rustc_server::Rustc::default(),
159 parsed_body,
160 );
161 return res.map(|it| it.subtree);
162 }
163 bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => {
164 let res = client.run(
165 &crate::proc_macro::bridge::server::SameThread,
166 crate::rustc_server::Rustc::default(),
167 parsed_body,
168 );
169 return res.map(|it| it.subtree);
170 }
171 bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => {
172 let res = client.run(
173 &crate::proc_macro::bridge::server::SameThread,
174 crate::rustc_server::Rustc::default(),
175 parsed_attributes,
176 parsed_body,
177 );
178
179 return res.map(|it| it.subtree);
180 }
181 _ => continue,
182 }
183 }
184 }
185
186 Err(bridge::PanicMessage::String("Nothing to expand".to_string()))
187 }
188
189 pub fn list_macros(&self) -> Result<Vec<(String, ProcMacroKind)>, bridge::PanicMessage> {
190 let mut result = vec![];
191
192 for lib in &self.libs {
193 for proc_macro in &lib.exported_macros {
194 let res = match proc_macro {
195 bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
196 (trait_name.to_string(), ProcMacroKind::CustomDerive)
197 }
198 bridge::client::ProcMacro::Bang { name, .. } => {
199 (name.to_string(), ProcMacroKind::FuncLike)
200 }
201 bridge::client::ProcMacro::Attr { name, .. } => {
202 (name.to_string(), ProcMacroKind::Attr)
203 }
204 };
205 result.push(res);
206 }
207 }
208
209 Ok(result)
210 }
211}
diff --git a/crates/ra_proc_macro_srv/src/lib.rs b/crates/ra_proc_macro_srv/src/lib.rs
index f376df236..59716cbb3 100644
--- a/crates/ra_proc_macro_srv/src/lib.rs
+++ b/crates/ra_proc_macro_srv/src/lib.rs
@@ -17,13 +17,41 @@ mod proc_macro;
17#[doc(hidden)] 17#[doc(hidden)]
18mod rustc_server; 18mod rustc_server;
19 19
20mod dylib;
21
20use proc_macro::bridge::client::TokenStream; 22use proc_macro::bridge::client::TokenStream;
21use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask}; 23use ra_proc_macro::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
22 24
23pub fn expand_task(_task: &ExpansionTask) -> Result<ExpansionResult, String> { 25pub fn expand_task(task: &ExpansionTask) -> Result<ExpansionResult, String> {
24 unimplemented!() 26 let expander = dylib::Expander::new(&task.lib)
27 .expect(&format!("Cannot expand with provided libraries: ${:?}", &task.lib));
28
29 match expander.expand(&task.macro_name, &task.macro_body, task.attributes.as_ref()) {
30 Ok(expansion) => Ok(ExpansionResult { expansion }),
31 Err(msg) => {
32 let reason = format!(
33 "Cannot perform expansion for {}: error {:?}!",
34 &task.macro_name,
35 msg.as_str()
36 );
37 Err(reason)
38 }
39 }
25} 40}
26 41
27pub fn list_macros(_task: &ListMacrosTask) -> Result<ListMacrosResult, String> { 42pub fn list_macros(task: &ListMacrosTask) -> Result<ListMacrosResult, String> {
28 unimplemented!() 43 let expander = dylib::Expander::new(&task.lib)
44 .expect(&format!("Cannot expand with provided libraries: ${:?}", &task.lib));
45
46 match expander.list_macros() {
47 Ok(macros) => Ok(ListMacrosResult { macros }),
48 Err(msg) => {
49 let reason =
50 format!("Cannot perform expansion for {:?}: error {:?}!", &task.lib, msg.as_str());
51 Err(reason)
52 }
53 }
29} 54}
55
56#[cfg(test)]
57mod tests;
diff --git a/crates/ra_proc_macro_srv/src/rustc_server.rs b/crates/ra_proc_macro_srv/src/rustc_server.rs
index 92d1fd989..ec0d35692 100644
--- a/crates/ra_proc_macro_srv/src/rustc_server.rs
+++ b/crates/ra_proc_macro_srv/src/rustc_server.rs
@@ -34,6 +34,10 @@ impl TokenStream {
34 TokenStream { subtree: Default::default() } 34 TokenStream { subtree: Default::default() }
35 } 35 }
36 36
37 pub fn with_subtree(subtree: tt::Subtree) -> Self {
38 TokenStream { subtree }
39 }
40
37 pub fn is_empty(&self) -> bool { 41 pub fn is_empty(&self) -> bool {
38 self.subtree.token_trees.is_empty() 42 self.subtree.token_trees.is_empty()
39 } 43 }
diff --git a/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt b/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt
new file mode 100644
index 000000000..24507d98d
--- /dev/null
+++ b/crates/ra_proc_macro_srv/src/tests/fixtures/test_serialize_proc_macro.txt
@@ -0,0 +1,188 @@
1SUBTREE $
2 PUNCH # [alone] 4294967295
3 SUBTREE [] 4294967295
4 IDENT allow 4294967295
5 SUBTREE () 4294967295
6 IDENT non_upper_case_globals 4294967295
7 PUNCH , [alone] 4294967295
8 IDENT unused_attributes 4294967295
9 PUNCH , [alone] 4294967295
10 IDENT unused_qualifications 4294967295
11 IDENT const 4294967295
12 IDENT _IMPL_SERIALIZE_FOR_Foo 4294967295
13 PUNCH : [alone] 4294967295
14 SUBTREE () 4294967295
15 PUNCH = [alone] 4294967295
16 SUBTREE {} 4294967295
17 PUNCH # [alone] 4294967295
18 SUBTREE [] 4294967295
19 IDENT allow 4294967295
20 SUBTREE () 4294967295
21 IDENT unknown_lints 4294967295
22 PUNCH # [alone] 4294967295
23 SUBTREE [] 4294967295
24 IDENT cfg_attr 4294967295
25 SUBTREE () 4294967295
26 IDENT feature 4294967295
27 PUNCH = [alone] 4294967295
28 SUBTREE $
29 LITERAL "cargo-clippy" 0
30 PUNCH , [alone] 4294967295
31 IDENT allow 4294967295
32 SUBTREE () 4294967295
33 IDENT useless_attribute 4294967295
34 PUNCH # [alone] 4294967295
35 SUBTREE [] 4294967295
36 IDENT allow 4294967295
37 SUBTREE () 4294967295
38 IDENT rust_2018_idioms 4294967295
39 IDENT extern 4294967295
40 IDENT crate 4294967295
41 IDENT serde 4294967295
42 IDENT as 4294967295
43 IDENT _serde 4294967295
44 PUNCH ; [alone] 4294967295
45 PUNCH # [alone] 4294967295
46 SUBTREE [] 4294967295
47 IDENT allow 4294967295
48 SUBTREE () 4294967295
49 IDENT unused_macros 4294967295
50 IDENT macro_rules 4294967295
51 PUNCH ! [alone] 4294967295
52 IDENT try 4294967295
53 SUBTREE {} 4294967295
54 SUBTREE () 4294967295
55 PUNCH $ [alone] 4294967295
56 IDENT __expr 4294967295
57 PUNCH : [alone] 4294967295
58 IDENT expr 4294967295
59 PUNCH = [joint] 4294967295
60 PUNCH > [alone] 4294967295
61 SUBTREE {} 4294967295
62 IDENT match 4294967295
63 PUNCH $ [alone] 4294967295
64 IDENT __expr 4294967295
65 SUBTREE {} 4294967295
66 IDENT _serde 4294967295
67 PUNCH : [joint] 4294967295
68 PUNCH : [alone] 4294967295
69 IDENT export 4294967295
70 PUNCH : [joint] 4294967295
71 PUNCH : [alone] 4294967295
72 IDENT Ok 4294967295
73 SUBTREE () 4294967295
74 IDENT __val 4294967295
75 PUNCH = [joint] 4294967295
76 PUNCH > [alone] 4294967295
77 IDENT __val 4294967295
78 PUNCH , [alone] 4294967295
79 IDENT _serde 4294967295
80 PUNCH : [joint] 4294967295
81 PUNCH : [alone] 4294967295
82 IDENT export 4294967295
83 PUNCH : [joint] 4294967295
84 PUNCH : [alone] 4294967295
85 IDENT Err 4294967295
86 SUBTREE () 4294967295
87 IDENT __err 4294967295
88 PUNCH = [joint] 4294967295
89 PUNCH > [alone] 4294967295
90 SUBTREE {} 4294967295
91 IDENT return 4294967295
92 IDENT _serde 4294967295
93 PUNCH : [joint] 4294967295
94 PUNCH : [alone] 4294967295
95 IDENT export 4294967295
96 PUNCH : [joint] 4294967295
97 PUNCH : [alone] 4294967295
98 IDENT Err 4294967295
99 SUBTREE () 4294967295
100 IDENT __err 4294967295
101 PUNCH ; [alone] 4294967295
102 PUNCH # [alone] 4294967295
103 SUBTREE [] 4294967295
104 IDENT automatically_derived 4294967295
105 IDENT impl 4294967295
106 IDENT _serde 4294967295
107 PUNCH : [joint] 4294967295
108 PUNCH : [alone] 4294967295
109 IDENT Serialize 4294967295
110 IDENT for 4294967295
111 IDENT Foo 1
112 SUBTREE {} 4294967295
113 IDENT fn 4294967295
114 IDENT serialize 4294967295
115 PUNCH < [alone] 4294967295
116 IDENT __S 4294967295
117 PUNCH > [alone] 4294967295
118 SUBTREE () 4294967295
119 PUNCH & [alone] 4294967295
120 IDENT self 4294967295
121 PUNCH , [alone] 4294967295
122 IDENT __serializer 4294967295
123 PUNCH : [alone] 4294967295
124 IDENT __S 4294967295
125 PUNCH - [joint] 4294967295
126 PUNCH > [alone] 4294967295
127 IDENT _serde 4294967295
128 PUNCH : [joint] 4294967295
129 PUNCH : [alone] 4294967295
130 IDENT export 4294967295
131 PUNCH : [joint] 4294967295
132 PUNCH : [alone] 4294967295
133 IDENT Result 4294967295
134 PUNCH < [alone] 4294967295
135 IDENT __S 4294967295
136 PUNCH : [joint] 4294967295
137 PUNCH : [alone] 4294967295
138 IDENT Ok 4294967295
139 PUNCH , [alone] 4294967295
140 IDENT __S 4294967295
141 PUNCH : [joint] 4294967295
142 PUNCH : [alone] 4294967295
143 IDENT Error 4294967295
144 PUNCH > [alone] 4294967295
145 IDENT where 4294967295
146 IDENT __S 4294967295
147 PUNCH : [alone] 4294967295
148 IDENT _serde 4294967295
149 PUNCH : [joint] 4294967295
150 PUNCH : [alone] 4294967295
151 IDENT Serializer 4294967295
152 PUNCH , [alone] 4294967295
153 SUBTREE {} 4294967295
154 IDENT let 4294967295
155 IDENT __serde_state 4294967295
156 PUNCH = [alone] 4294967295
157 IDENT try 4294967295
158 PUNCH ! [alone] 4294967295
159 SUBTREE () 4294967295
160 IDENT _serde 4294967295
161 PUNCH : [joint] 4294967295
162 PUNCH : [alone] 4294967295
163 IDENT Serializer 4294967295
164 PUNCH : [joint] 4294967295
165 PUNCH : [alone] 4294967295
166 IDENT serialize_struct 4294967295
167 SUBTREE () 4294967295
168 IDENT __serializer 4294967295
169 PUNCH , [alone] 4294967295
170 LITERAL "Foo" 4294967295
171 PUNCH , [alone] 4294967295
172 IDENT false 4294967295
173 IDENT as 4294967295
174 IDENT usize 4294967295
175 PUNCH ; [alone] 4294967295
176 IDENT _serde 4294967295
177 PUNCH : [joint] 4294967295
178 PUNCH : [alone] 4294967295
179 IDENT ser 4294967295
180 PUNCH : [joint] 4294967295
181 PUNCH : [alone] 4294967295
182 IDENT SerializeStruct 4294967295
183 PUNCH : [joint] 4294967295
184 PUNCH : [alone] 4294967295
185 IDENT end 4294967295
186 SUBTREE () 4294967295
187 IDENT __serde_state 4294967295
188 PUNCH ; [alone] 4294967295 \ No newline at end of file
diff --git a/crates/ra_proc_macro_srv/src/tests/mod.rs b/crates/ra_proc_macro_srv/src/tests/mod.rs
new file mode 100644
index 000000000..03f79bc5d
--- /dev/null
+++ b/crates/ra_proc_macro_srv/src/tests/mod.rs
@@ -0,0 +1,47 @@
1//! proc-macro tests
2
3#[macro_use]
4mod utils;
5use test_utils::assert_eq_text;
6use utils::*;
7
8#[test]
9fn test_derive_serialize_proc_macro() {
10 assert_expand(
11 "serde_derive",
12 "Serialize",
13 "1.0.104",
14 r##"struct Foo {}"##,
15 include_str!("fixtures/test_serialize_proc_macro.txt"),
16 );
17}
18
19#[test]
20fn test_derive_serialize_proc_macro_failed() {
21 assert_expand(
22 "serde_derive",
23 "Serialize",
24 "1.0.104",
25 r##"
26 struct {}
27"##,
28 r##"
29SUBTREE $
30 IDENT compile_error 4294967295
31 PUNCH ! [alone] 4294967295
32 SUBTREE {} 4294967295
33 LITERAL "expected identifier" 4294967295
34"##,
35 );
36}
37
38#[test]
39fn test_derive_proc_macro_list() {
40 let res = list("serde_derive", "1.0.104").join("\n");
41
42 assert_eq_text!(
43 &res,
44 r#"Serialize [CustomDerive]
45Deserialize [CustomDerive]"#
46 );
47}
diff --git a/crates/ra_proc_macro_srv/src/tests/utils.rs b/crates/ra_proc_macro_srv/src/tests/utils.rs
new file mode 100644
index 000000000..1ee409449
--- /dev/null
+++ b/crates/ra_proc_macro_srv/src/tests/utils.rs
@@ -0,0 +1,65 @@
1//! utils used in proc-macro tests
2
3use crate::dylib;
4use crate::list_macros;
5pub use difference::Changeset as __Changeset;
6use ra_proc_macro::ListMacrosTask;
7use std::str::FromStr;
8use test_utils::assert_eq_text;
9
10mod fixtures {
11 use cargo_metadata::{parse_messages, Message};
12 use std::process::Command;
13
14 // Use current project metadata to get the proc-macro dylib path
15 pub fn dylib_path(crate_name: &str, version: &str) -> std::path::PathBuf {
16 let command = Command::new("cargo")
17 .args(&["check", "--message-format", "json"])
18 .output()
19 .unwrap()
20 .stdout;
21
22 for message in parse_messages(command.as_slice()) {
23 match message.unwrap() {
24 Message::CompilerArtifact(artifact) => {
25 if artifact.target.kind.contains(&"proc-macro".to_string()) {
26 let repr = format!("{} {}", crate_name, version);
27 if artifact.package_id.repr.starts_with(&repr) {
28 return artifact.filenames[0].clone();
29 }
30 }
31 }
32 _ => (), // Unknown message
33 }
34 }
35
36 panic!("No proc-macro dylib for {} found!", crate_name);
37 }
38}
39
40fn parse_string(code: &str) -> Option<crate::rustc_server::TokenStream> {
41 Some(crate::rustc_server::TokenStream::from_str(code).unwrap())
42}
43
44pub fn assert_expand(
45 crate_name: &str,
46 macro_name: &str,
47 version: &str,
48 fixture: &str,
49 expect: &str,
50) {
51 let path = fixtures::dylib_path(crate_name, version);
52 let expander = dylib::Expander::new(&path).unwrap();
53 let fixture = parse_string(fixture).unwrap();
54
55 let res = expander.expand(macro_name, &fixture.subtree, None).unwrap();
56 assert_eq_text!(&format!("{:?}", res), &expect.trim());
57}
58
59pub fn list(crate_name: &str, version: &str) -> Vec<String> {
60 let path = fixtures::dylib_path(crate_name, version);
61 let task = ListMacrosTask { lib: path };
62
63 let res = list_macros(&task).unwrap();
64 res.macros.into_iter().map(|(name, kind)| format!("{} [{:?}]", name, kind)).collect()
65}
diff --git a/crates/ra_syntax/src/ptr.rs b/crates/ra_syntax/src/ptr.rs
index db6230aab..bc48a2e71 100644
--- a/crates/ra_syntax/src/ptr.rs
+++ b/crates/ra_syntax/src/ptr.rs
@@ -10,7 +10,7 @@ use crate::{AstNode, SyntaxKind, SyntaxNode, TextRange};
10 10
11/// A pointer to a syntax node inside a file. It can be used to remember a 11/// A pointer to a syntax node inside a file. It can be used to remember a
12/// specific node across reparses of the same file. 12/// specific node across reparses of the same file.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] 13#[derive(Debug, Clone, PartialEq, Eq, Hash)]
14pub struct SyntaxNodePtr { 14pub struct SyntaxNodePtr {
15 pub(crate) range: TextRange, 15 pub(crate) range: TextRange,
16 kind: SyntaxKind, 16 kind: SyntaxKind,
@@ -21,7 +21,7 @@ impl SyntaxNodePtr {
21 SyntaxNodePtr { range: node.text_range(), kind: node.kind() } 21 SyntaxNodePtr { range: node.text_range(), kind: node.kind() }
22 } 22 }
23 23
24 pub fn to_node(self, root: &SyntaxNode) -> SyntaxNode { 24 pub fn to_node(&self, root: &SyntaxNode) -> SyntaxNode {
25 assert!(root.parent().is_none()); 25 assert!(root.parent().is_none());
26 successors(Some(root.clone()), |node| { 26 successors(Some(root.clone()), |node| {
27 node.children().find(|it| self.range.is_subrange(&it.text_range())) 27 node.children().find(|it| self.range.is_subrange(&it.text_range()))
@@ -30,11 +30,11 @@ impl SyntaxNodePtr {
30 .unwrap_or_else(|| panic!("can't resolve local ptr to SyntaxNode: {:?}", self)) 30 .unwrap_or_else(|| panic!("can't resolve local ptr to SyntaxNode: {:?}", self))
31 } 31 }
32 32
33 pub fn range(self) -> TextRange { 33 pub fn range(&self) -> TextRange {
34 self.range 34 self.range
35 } 35 }
36 36
37 pub fn kind(self) -> SyntaxKind { 37 pub fn kind(&self) -> SyntaxKind {
38 self.kind 38 self.kind
39 } 39 }
40 40
@@ -53,10 +53,9 @@ pub struct AstPtr<N: AstNode> {
53 _ty: PhantomData<fn() -> N>, 53 _ty: PhantomData<fn() -> N>,
54} 54}
55 55
56impl<N: AstNode> Copy for AstPtr<N> {}
57impl<N: AstNode> Clone for AstPtr<N> { 56impl<N: AstNode> Clone for AstPtr<N> {
58 fn clone(&self) -> AstPtr<N> { 57 fn clone(&self) -> AstPtr<N> {
59 *self 58 AstPtr { raw: self.raw.clone(), _ty: PhantomData }
60 } 59 }
61} 60}
62 61
@@ -79,13 +78,13 @@ impl<N: AstNode> AstPtr<N> {
79 AstPtr { raw: SyntaxNodePtr::new(node.syntax()), _ty: PhantomData } 78 AstPtr { raw: SyntaxNodePtr::new(node.syntax()), _ty: PhantomData }
80 } 79 }
81 80
82 pub fn to_node(self, root: &SyntaxNode) -> N { 81 pub fn to_node(&self, root: &SyntaxNode) -> N {
83 let syntax_node = self.raw.to_node(root); 82 let syntax_node = self.raw.to_node(root);
84 N::cast(syntax_node).unwrap() 83 N::cast(syntax_node).unwrap()
85 } 84 }
86 85
87 pub fn syntax_node_ptr(self) -> SyntaxNodePtr { 86 pub fn syntax_node_ptr(&self) -> SyntaxNodePtr {
88 self.raw 87 self.raw.clone()
89 } 88 }
90 89
91 pub fn cast<U: AstNode>(self) -> Option<AstPtr<U>> { 90 pub fn cast<U: AstNode>(self) -> Option<AstPtr<U>> {
diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs
index 69133e4e4..43062ea10 100644
--- a/crates/rust-analyzer/src/cli/load_cargo.rs
+++ b/crates/rust-analyzer/src/cli/load_cargo.rs
@@ -152,7 +152,9 @@ pub(crate) fn load(
152 &extern_source_roots, 152 &extern_source_roots,
153 proc_macro_client, 153 proc_macro_client,
154 &mut |path: &Path| { 154 &mut |path: &Path| {
155 let vfs_file = vfs.load(path); 155 // Some path from metadata will be non canonicalized, e.g. /foo/../bar/lib.rs
156 let path = path.canonicalize().ok()?;
157 let vfs_file = vfs.load(&path);
156 log::debug!("vfs file {:?} -> {:?}", path, vfs_file); 158 log::debug!("vfs file {:?} -> {:?}", path, vfs_file);
157 vfs_file.map(vfs_file_to_id) 159 vfs_file.map(vfs_file_to_id)
158 }, 160 },
diff --git a/crates/rust-analyzer/src/world.rs b/crates/rust-analyzer/src/world.rs
index 365f57d8c..6c42e1d76 100644
--- a/crates/rust-analyzer/src/world.rs
+++ b/crates/rust-analyzer/src/world.rs
@@ -139,7 +139,9 @@ impl WorldState {
139 // Create crate graph from all the workspaces 139 // Create crate graph from all the workspaces
140 let mut crate_graph = CrateGraph::default(); 140 let mut crate_graph = CrateGraph::default();
141 let mut load = |path: &std::path::Path| { 141 let mut load = |path: &std::path::Path| {
142 let vfs_file = vfs.load(path); 142 // Some path from metadata will be non canonicalized, e.g. /foo/../bar/lib.rs
143 let path = path.canonicalize().ok()?;
144 let vfs_file = vfs.load(&path);
143 vfs_file.map(|f| FileId(f.0)) 145 vfs_file.map(|f| FileId(f.0))
144 }; 146 };
145 147