aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir/src/source_binder.rs12
-rw-r--r--crates/ra_hir_expand/src/builtin_macro.rs58
-rw-r--r--crates/ra_hir_expand/src/db.rs4
-rw-r--r--crates/ra_hir_expand/src/lib.rs39
-rw-r--r--crates/ra_hir_expand/src/name.rs116
-rw-r--r--crates/ra_ide/src/goto_definition.rs30
6 files changed, 194 insertions, 65 deletions
diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs
index 42c392513..0e136b904 100644
--- a/crates/ra_hir/src/source_binder.rs
+++ b/crates/ra_hir/src/source_binder.rs
@@ -76,12 +76,13 @@ fn def_with_body_from_child_node(
76 db: &impl HirDatabase, 76 db: &impl HirDatabase,
77 child: InFile<&SyntaxNode>, 77 child: InFile<&SyntaxNode>,
78) -> Option<DefWithBody> { 78) -> Option<DefWithBody> {
79 child.value.ancestors().find_map(|node| { 79 child.cloned().ancestors_with_macros(db).find_map(|node| {
80 let n = &node.value;
80 match_ast! { 81 match_ast! {
81 match node { 82 match n {
82 ast::FnDef(def) => { return Function::from_source(db, child.with_value(def)).map(DefWithBody::from); }, 83 ast::FnDef(def) => { return Function::from_source(db, node.with_value(def)).map(DefWithBody::from); },
83 ast::ConstDef(def) => { return Const::from_source(db, child.with_value(def)).map(DefWithBody::from); }, 84 ast::ConstDef(def) => { return Const::from_source(db, node.with_value(def)).map(DefWithBody::from); },
84 ast::StaticDef(def) => { return Static::from_source(db, child.with_value(def)).map(DefWithBody::from); }, 85 ast::StaticDef(def) => { return Static::from_source(db, node.with_value(def)).map(DefWithBody::from); },
85 _ => { None }, 86 _ => { None },
86 } 87 }
87 } 88 }
@@ -135,6 +136,7 @@ pub struct ReferenceDescriptor {
135 pub name: String, 136 pub name: String,
136} 137}
137 138
139#[derive(Debug)]
138pub struct Expansion { 140pub struct Expansion {
139 macro_file_kind: MacroFileKind, 141 macro_file_kind: MacroFileKind,
140 macro_call_id: MacroCallId, 142 macro_call_id: MacroCallId,
diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs
index 35f99b2bc..99303188b 100644
--- a/crates/ra_hir_expand/src/builtin_macro.rs
+++ b/crates/ra_hir_expand/src/builtin_macro.rs
@@ -49,7 +49,11 @@ register_builtin! {
49 (COMPILE_ERROR_MACRO, CompileError) => compile_error_expand, 49 (COMPILE_ERROR_MACRO, CompileError) => compile_error_expand,
50 (FILE_MACRO, File) => file_expand, 50 (FILE_MACRO, File) => file_expand,
51 (LINE_MACRO, Line) => line_expand, 51 (LINE_MACRO, Line) => line_expand,
52 (STRINGIFY_MACRO, Stringify) => stringify_expand 52 (STRINGIFY_MACRO, Stringify) => stringify_expand,
53 (FORMAT_ARGS_MACRO, FormatArgs) => format_args_expand,
54 // format_args_nl only differs in that it adds a newline in the end,
55 // so we use the same stub expansion for now
56 (FORMAT_ARGS_NL_MACRO, FormatArgsNl) => format_args_expand
53} 57}
54 58
55fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { 59fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
@@ -200,6 +204,41 @@ fn compile_error_expand(
200 Err(mbe::ExpandError::BindingError("Must be a string".into())) 204 Err(mbe::ExpandError::BindingError("Must be a string".into()))
201} 205}
202 206
207fn format_args_expand(
208 _db: &dyn AstDatabase,
209 _id: MacroCallId,
210 tt: &tt::Subtree,
211) -> Result<tt::Subtree, mbe::ExpandError> {
212 // We expand `format_args!("", arg1, arg2)` to
213 // `std::fmt::Arguments::new_v1(&[], &[&arg1, &arg2])`,
214 // which is still not really correct, but close enough for now
215 let mut args = Vec::new();
216 let mut current = Vec::new();
217 for tt in tt.token_trees.iter().cloned() {
218 match tt {
219 tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
220 args.push(tt::Subtree { delimiter: tt::Delimiter::None, token_trees: current });
221 current = Vec::new();
222 }
223 _ => {
224 current.push(tt);
225 }
226 }
227 }
228 if !current.is_empty() {
229 args.push(tt::Subtree { delimiter: tt::Delimiter::None, token_trees: current });
230 }
231 if args.is_empty() {
232 return Err(mbe::ExpandError::NoMatchingRule);
233 }
234 let _format_string = args.remove(0);
235 let arg_tts = args.into_iter().flat_map(|arg| (quote! { & #arg , }).token_trees);
236 let expanded = quote! {
237 std::fmt::Arguments::new_v1(&[], &[##arg_tts])
238 };
239 Ok(expanded)
240}
241
203#[cfg(test)] 242#[cfg(test)]
204mod tests { 243mod tests {
205 use super::*; 244 use super::*;
@@ -307,4 +346,21 @@ mod tests {
307 346
308 assert_eq!(expanded, r#"loop{"error!"}"#); 347 assert_eq!(expanded, r#"loop{"error!"}"#);
309 } 348 }
349
350 #[test]
351 fn test_format_args_expand() {
352 let expanded = expand_builtin_macro(
353 r#"
354 #[rustc_builtin_macro]
355 macro_rules! format_args {
356 ($fmt:expr) => ({ /* compiler built-in */ });
357 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
358 }
359 format_args!("{} {:?}", arg1(a, b, c), arg2);
360"#,
361 BuiltinFnLikeExpander::FormatArgs,
362 );
363
364 assert_eq!(expanded, r#"std::fmt::Arguments::new_v1(&[] ,&[&arg1(a,b,c),&arg2,])"#);
365 }
310} 366}
diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs
index 99dabf3fb..013a6c8ba 100644
--- a/crates/ra_hir_expand/src/db.rs
+++ b/crates/ra_hir_expand/src/db.rs
@@ -45,8 +45,8 @@ impl TokenExpander {
45 pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) { 45 pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) {
46 match self { 46 match self {
47 TokenExpander::MacroRules(it) => it.map_id_up(id), 47 TokenExpander::MacroRules(it) => it.map_id_up(id),
48 TokenExpander::Builtin(..) => (id, mbe::Origin::Def), 48 TokenExpander::Builtin(..) => (id, mbe::Origin::Call),
49 TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Def), 49 TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call),
50 } 50 }
51 } 51 }
52} 52}
diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs
index 59c69b91b..0a5da7e54 100644
--- a/crates/ra_hir_expand/src/lib.rs
+++ b/crates/ra_hir_expand/src/lib.rs
@@ -76,6 +76,17 @@ impl HirFileId {
76 } 76 }
77 } 77 }
78 78
79 /// If this is a macro call, returns the syntax node of the call.
80 pub fn call_node(self, db: &dyn db::AstDatabase) -> Option<InFile<SyntaxNode>> {
81 match self.0 {
82 HirFileIdRepr::FileId(_) => None,
83 HirFileIdRepr::MacroFile(macro_file) => {
84 let loc = db.lookup_intern_macro(macro_file.macro_call_id);
85 Some(loc.kind.node(db))
86 }
87 }
88 }
89
79 /// Return expansion information if it is a macro-expansion file 90 /// Return expansion information if it is a macro-expansion file
80 pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option<ExpansionInfo> { 91 pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option<ExpansionInfo> {
81 match self.0 { 92 match self.0 {
@@ -176,6 +187,13 @@ impl MacroCallKind {
176 } 187 }
177 } 188 }
178 189
190 pub fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> {
191 match self {
192 MacroCallKind::FnLike(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()),
193 MacroCallKind::Attr(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()),
194 }
195 }
196
179 pub fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> { 197 pub fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> {
180 match self { 198 match self {
181 MacroCallKind::FnLike(ast_id) => { 199 MacroCallKind::FnLike(ast_id) => {
@@ -283,3 +301,24 @@ impl<T> InFile<T> {
283 db.parse_or_expand(self.file_id).expect("source created from invalid file") 301 db.parse_or_expand(self.file_id).expect("source created from invalid file")
284 } 302 }
285} 303}
304
305impl<T: Clone> InFile<&T> {
306 pub fn cloned(&self) -> InFile<T> {
307 self.with_value(self.value.clone())
308 }
309}
310
311impl InFile<SyntaxNode> {
312 pub fn ancestors_with_macros<'a>(
313 self,
314 db: &'a impl crate::db::AstDatabase,
315 ) -> impl Iterator<Item = InFile<SyntaxNode>> + 'a {
316 std::iter::successors(Some(self), move |node| match node.value.parent() {
317 Some(parent) => Some(node.with_value(parent)),
318 None => {
319 let parent_node = node.file_id.call_node(db)?;
320 Some(parent_node)
321 }
322 })
323 }
324}
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index c5a191160..4f2f702c0 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -38,8 +38,8 @@ impl Name {
38 } 38 }
39 39
40 /// Shortcut to create inline plain text name 40 /// Shortcut to create inline plain text name
41 const fn new_inline_ascii(len: usize, text: &[u8]) -> Name { 41 const fn new_inline_ascii(text: &[u8]) -> Name {
42 Name::new_text(SmolStr::new_inline_from_ascii(len, text)) 42 Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text))
43 } 43 }
44 44
45 /// Resolve a name from the text of token. 45 /// Resolve a name from the text of token.
@@ -105,68 +105,70 @@ impl AsName for ra_db::Dependency {
105} 105}
106 106
107// Primitives 107// Primitives
108pub const ISIZE: Name = Name::new_inline_ascii(5, b"isize"); 108pub const ISIZE: Name = Name::new_inline_ascii(b"isize");
109pub const I8: Name = Name::new_inline_ascii(2, b"i8"); 109pub const I8: Name = Name::new_inline_ascii(b"i8");
110pub const I16: Name = Name::new_inline_ascii(3, b"i16"); 110pub const I16: Name = Name::new_inline_ascii(b"i16");
111pub const I32: Name = Name::new_inline_ascii(3, b"i32"); 111pub const I32: Name = Name::new_inline_ascii(b"i32");
112pub const I64: Name = Name::new_inline_ascii(3, b"i64"); 112pub const I64: Name = Name::new_inline_ascii(b"i64");
113pub const I128: Name = Name::new_inline_ascii(4, b"i128"); 113pub const I128: Name = Name::new_inline_ascii(b"i128");
114pub const USIZE: Name = Name::new_inline_ascii(5, b"usize"); 114pub const USIZE: Name = Name::new_inline_ascii(b"usize");
115pub const U8: Name = Name::new_inline_ascii(2, b"u8"); 115pub const U8: Name = Name::new_inline_ascii(b"u8");
116pub const U16: Name = Name::new_inline_ascii(3, b"u16"); 116pub const U16: Name = Name::new_inline_ascii(b"u16");
117pub const U32: Name = Name::new_inline_ascii(3, b"u32"); 117pub const U32: Name = Name::new_inline_ascii(b"u32");
118pub const U64: Name = Name::new_inline_ascii(3, b"u64"); 118pub const U64: Name = Name::new_inline_ascii(b"u64");
119pub const U128: Name = Name::new_inline_ascii(4, b"u128"); 119pub const U128: Name = Name::new_inline_ascii(b"u128");
120pub const F32: Name = Name::new_inline_ascii(3, b"f32"); 120pub const F32: Name = Name::new_inline_ascii(b"f32");
121pub const F64: Name = Name::new_inline_ascii(3, b"f64"); 121pub const F64: Name = Name::new_inline_ascii(b"f64");
122pub const BOOL: Name = Name::new_inline_ascii(4, b"bool"); 122pub const BOOL: Name = Name::new_inline_ascii(b"bool");
123pub const CHAR: Name = Name::new_inline_ascii(4, b"char"); 123pub const CHAR: Name = Name::new_inline_ascii(b"char");
124pub const STR: Name = Name::new_inline_ascii(3, b"str"); 124pub const STR: Name = Name::new_inline_ascii(b"str");
125 125
126// Special names 126// Special names
127pub const SELF_PARAM: Name = Name::new_inline_ascii(4, b"self"); 127pub const SELF_PARAM: Name = Name::new_inline_ascii(b"self");
128pub const SELF_TYPE: Name = Name::new_inline_ascii(4, b"Self"); 128pub const SELF_TYPE: Name = Name::new_inline_ascii(b"Self");
129pub const MACRO_RULES: Name = Name::new_inline_ascii(11, b"macro_rules"); 129pub const MACRO_RULES: Name = Name::new_inline_ascii(b"macro_rules");
130 130
131// Components of known path (value or mod name) 131// Components of known path (value or mod name)
132pub const STD: Name = Name::new_inline_ascii(3, b"std"); 132pub const STD: Name = Name::new_inline_ascii(b"std");
133pub const ITER: Name = Name::new_inline_ascii(4, b"iter"); 133pub const ITER: Name = Name::new_inline_ascii(b"iter");
134pub const OPS: Name = Name::new_inline_ascii(3, b"ops"); 134pub const OPS: Name = Name::new_inline_ascii(b"ops");
135pub const FUTURE: Name = Name::new_inline_ascii(6, b"future"); 135pub const FUTURE: Name = Name::new_inline_ascii(b"future");
136pub const RESULT: Name = Name::new_inline_ascii(6, b"result"); 136pub const RESULT: Name = Name::new_inline_ascii(b"result");
137pub const BOXED: Name = Name::new_inline_ascii(5, b"boxed"); 137pub const BOXED: Name = Name::new_inline_ascii(b"boxed");
138 138
139// Components of known path (type name) 139// Components of known path (type name)
140pub const INTO_ITERATOR_TYPE: Name = Name::new_inline_ascii(12, b"IntoIterator"); 140pub const INTO_ITERATOR_TYPE: Name = Name::new_inline_ascii(b"IntoIterator");
141pub const ITEM_TYPE: Name = Name::new_inline_ascii(4, b"Item"); 141pub const ITEM_TYPE: Name = Name::new_inline_ascii(b"Item");
142pub const TRY_TYPE: Name = Name::new_inline_ascii(3, b"Try"); 142pub const TRY_TYPE: Name = Name::new_inline_ascii(b"Try");
143pub const OK_TYPE: Name = Name::new_inline_ascii(2, b"Ok"); 143pub const OK_TYPE: Name = Name::new_inline_ascii(b"Ok");
144pub const FUTURE_TYPE: Name = Name::new_inline_ascii(6, b"Future"); 144pub const FUTURE_TYPE: Name = Name::new_inline_ascii(b"Future");
145pub const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result"); 145pub const RESULT_TYPE: Name = Name::new_inline_ascii(b"Result");
146pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output"); 146pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(b"Output");
147pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target"); 147pub const TARGET_TYPE: Name = Name::new_inline_ascii(b"Target");
148pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box"); 148pub const BOX_TYPE: Name = Name::new_inline_ascii(b"Box");
149pub const RANGE_FROM_TYPE: Name = Name::new_inline_ascii(9, b"RangeFrom"); 149pub const RANGE_FROM_TYPE: Name = Name::new_inline_ascii(b"RangeFrom");
150pub const RANGE_FULL_TYPE: Name = Name::new_inline_ascii(9, b"RangeFull"); 150pub const RANGE_FULL_TYPE: Name = Name::new_inline_ascii(b"RangeFull");
151pub const RANGE_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(14, b"RangeInclusive"); 151pub const RANGE_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(b"RangeInclusive");
152pub const RANGE_TO_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(16, b"RangeToInclusive"); 152pub const RANGE_TO_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(b"RangeToInclusive");
153pub const RANGE_TO_TYPE: Name = Name::new_inline_ascii(7, b"RangeTo"); 153pub const RANGE_TO_TYPE: Name = Name::new_inline_ascii(b"RangeTo");
154pub const RANGE_TYPE: Name = Name::new_inline_ascii(5, b"Range"); 154pub const RANGE_TYPE: Name = Name::new_inline_ascii(b"Range");
155 155
156// Builtin Macros 156// Builtin Macros
157pub const FILE_MACRO: Name = Name::new_inline_ascii(4, b"file"); 157pub const FILE_MACRO: Name = Name::new_inline_ascii(b"file");
158pub const COLUMN_MACRO: Name = Name::new_inline_ascii(6, b"column"); 158pub const COLUMN_MACRO: Name = Name::new_inline_ascii(b"column");
159pub const COMPILE_ERROR_MACRO: Name = Name::new_inline_ascii(13, b"compile_error"); 159pub const COMPILE_ERROR_MACRO: Name = Name::new_inline_ascii(b"compile_error");
160pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line"); 160pub const LINE_MACRO: Name = Name::new_inline_ascii(b"line");
161pub const STRINGIFY_MACRO: Name = Name::new_inline_ascii(9, b"stringify"); 161pub const STRINGIFY_MACRO: Name = Name::new_inline_ascii(b"stringify");
162pub const FORMAT_ARGS_MACRO: Name = Name::new_inline_ascii(b"format_args");
163pub const FORMAT_ARGS_NL_MACRO: Name = Name::new_inline_ascii(b"format_args_nl");
162 164
163// Builtin derives 165// Builtin derives
164pub const COPY_TRAIT: Name = Name::new_inline_ascii(4, b"Copy"); 166pub const COPY_TRAIT: Name = Name::new_inline_ascii(b"Copy");
165pub const CLONE_TRAIT: Name = Name::new_inline_ascii(5, b"Clone"); 167pub const CLONE_TRAIT: Name = Name::new_inline_ascii(b"Clone");
166pub const DEFAULT_TRAIT: Name = Name::new_inline_ascii(7, b"Default"); 168pub const DEFAULT_TRAIT: Name = Name::new_inline_ascii(b"Default");
167pub const DEBUG_TRAIT: Name = Name::new_inline_ascii(5, b"Debug"); 169pub const DEBUG_TRAIT: Name = Name::new_inline_ascii(b"Debug");
168pub const HASH_TRAIT: Name = Name::new_inline_ascii(4, b"Hash"); 170pub const HASH_TRAIT: Name = Name::new_inline_ascii(b"Hash");
169pub const ORD_TRAIT: Name = Name::new_inline_ascii(3, b"Ord"); 171pub const ORD_TRAIT: Name = Name::new_inline_ascii(b"Ord");
170pub const PARTIAL_ORD_TRAIT: Name = Name::new_inline_ascii(10, b"PartialOrd"); 172pub const PARTIAL_ORD_TRAIT: Name = Name::new_inline_ascii(b"PartialOrd");
171pub const EQ_TRAIT: Name = Name::new_inline_ascii(2, b"Eq"); 173pub const EQ_TRAIT: Name = Name::new_inline_ascii(b"Eq");
172pub const PARTIAL_EQ_TRAIT: Name = Name::new_inline_ascii(9, b"PartialEq"); 174pub const PARTIAL_EQ_TRAIT: Name = Name::new_inline_ascii(b"PartialEq");
diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs
index 76a741207..d3c198813 100644
--- a/crates/ra_ide/src/goto_definition.rs
+++ b/crates/ra_ide/src/goto_definition.rs
@@ -689,8 +689,38 @@ mod tests {
689 fo<|>o(); 689 fo<|>o();
690 } 690 }
691 } 691 }
692 mod confuse_index { fn foo(); }
692 ", 693 ",
693 "foo FN_DEF FileId(1) [52; 63) [55; 58)", 694 "foo FN_DEF FileId(1) [52; 63) [55; 58)",
694 ); 695 );
695 } 696 }
697
698 #[should_panic] // currently failing because of expr mapping problems
699 #[test]
700 fn goto_through_format() {
701 check_goto(
702 "
703 //- /lib.rs
704 #[macro_export]
705 macro_rules! format {
706 ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*)))
707 }
708 #[rustc_builtin_macro]
709 #[macro_export]
710 macro_rules! format_args {
711 ($fmt:expr) => ({ /* compiler built-in */ });
712 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
713 }
714 pub mod __export {
715 pub use crate::format_args;
716 fn foo() {} // for index confusion
717 }
718 fn foo() -> i8 {}
719 fn test() {
720 format!(\"{}\", fo<|>o())
721 }
722 ",
723 "foo FN_DEF FileId(1) [359; 376) [362; 365)",
724 );
725 }
696} 726}