From c5ffb0dc815358712a42f9358cc3538f9a7b3014 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 6 Dec 2019 10:57:20 +0100 Subject: Add stub implementation of format_args{_nl} macros Just enough to fix the huge amount of type mismatches they cause. --- crates/ra_hir_expand/src/builtin_macro.rs | 19 ++++++++++++++++++- crates/ra_hir_expand/src/name.rs | 2 ++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index 35f99b2bc..e0709704a 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! { (COMPILE_ERROR_MACRO, CompileError) => compile_error_expand, (FILE_MACRO, File) => file_expand, (LINE_MACRO, Line) => line_expand, - (STRINGIFY_MACRO, Stringify) => stringify_expand + (STRINGIFY_MACRO, Stringify) => stringify_expand, + (FORMAT_ARGS_MACRO, FormatArgs) => format_args_expand, + // format_args_nl only differs in that it adds a newline in the end, + // so we use the same stub expansion for now + (FORMAT_ARGS_NL_MACRO, FormatArgsNl) => format_args_expand } fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize { @@ -200,6 +204,19 @@ fn compile_error_expand( Err(mbe::ExpandError::BindingError("Must be a string".into())) } +fn format_args_expand( + _db: &dyn AstDatabase, + _id: MacroCallId, + _tt: &tt::Subtree, +) -> Result { + // FIXME this is just a stub to make format macros type-check without mismatches + // We should make this at least insert the arguments, so that go to def etc. work within format macros + let expanded = quote! { + std::fmt::Arguments::new_v1(&[], &[]) + }; + Ok(expanded) +} + #[cfg(test)] mod tests { use super::*; diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs index c5a191160..34edf2003 100644 --- a/crates/ra_hir_expand/src/name.rs +++ b/crates/ra_hir_expand/src/name.rs @@ -159,6 +159,8 @@ pub const COLUMN_MACRO: Name = Name::new_inline_ascii(6, b"column"); pub const COMPILE_ERROR_MACRO: Name = Name::new_inline_ascii(13, b"compile_error"); pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line"); pub const STRINGIFY_MACRO: Name = Name::new_inline_ascii(9, b"stringify"); +pub const FORMAT_ARGS_MACRO: Name = Name::new_inline_ascii(11, b"format_args"); +pub const FORMAT_ARGS_NL_MACRO: Name = Name::new_inline_ascii(14, b"format_args_nl"); // Builtin derives pub const COPY_TRAIT: Name = Name::new_inline_ascii(4, b"Copy"); -- cgit v1.2.3 From 3a5aa03e66f1b46218f152f1e3e3db3bb1bd8077 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 6 Dec 2019 10:59:10 +0100 Subject: Remove unnecessary len parameter for Name::new_inline_ascii I assume it was previously required because `len` was not const, but that doesn't seem to be a problem anymore. --- crates/ra_hir_expand/src/name.rs | 118 +++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs index 34edf2003..4f2f702c0 100644 --- a/crates/ra_hir_expand/src/name.rs +++ b/crates/ra_hir_expand/src/name.rs @@ -38,8 +38,8 @@ impl Name { } /// Shortcut to create inline plain text name - const fn new_inline_ascii(len: usize, text: &[u8]) -> Name { - Name::new_text(SmolStr::new_inline_from_ascii(len, text)) + const fn new_inline_ascii(text: &[u8]) -> Name { + Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text)) } /// Resolve a name from the text of token. @@ -105,70 +105,70 @@ impl AsName for ra_db::Dependency { } // Primitives -pub const ISIZE: Name = Name::new_inline_ascii(5, b"isize"); -pub const I8: Name = Name::new_inline_ascii(2, b"i8"); -pub const I16: Name = Name::new_inline_ascii(3, b"i16"); -pub const I32: Name = Name::new_inline_ascii(3, b"i32"); -pub const I64: Name = Name::new_inline_ascii(3, b"i64"); -pub const I128: Name = Name::new_inline_ascii(4, b"i128"); -pub const USIZE: Name = Name::new_inline_ascii(5, b"usize"); -pub const U8: Name = Name::new_inline_ascii(2, b"u8"); -pub const U16: Name = Name::new_inline_ascii(3, b"u16"); -pub const U32: Name = Name::new_inline_ascii(3, b"u32"); -pub const U64: Name = Name::new_inline_ascii(3, b"u64"); -pub const U128: Name = Name::new_inline_ascii(4, b"u128"); -pub const F32: Name = Name::new_inline_ascii(3, b"f32"); -pub const F64: Name = Name::new_inline_ascii(3, b"f64"); -pub const BOOL: Name = Name::new_inline_ascii(4, b"bool"); -pub const CHAR: Name = Name::new_inline_ascii(4, b"char"); -pub const STR: Name = Name::new_inline_ascii(3, b"str"); +pub const ISIZE: Name = Name::new_inline_ascii(b"isize"); +pub const I8: Name = Name::new_inline_ascii(b"i8"); +pub const I16: Name = Name::new_inline_ascii(b"i16"); +pub const I32: Name = Name::new_inline_ascii(b"i32"); +pub const I64: Name = Name::new_inline_ascii(b"i64"); +pub const I128: Name = Name::new_inline_ascii(b"i128"); +pub const USIZE: Name = Name::new_inline_ascii(b"usize"); +pub const U8: Name = Name::new_inline_ascii(b"u8"); +pub const U16: Name = Name::new_inline_ascii(b"u16"); +pub const U32: Name = Name::new_inline_ascii(b"u32"); +pub const U64: Name = Name::new_inline_ascii(b"u64"); +pub const U128: Name = Name::new_inline_ascii(b"u128"); +pub const F32: Name = Name::new_inline_ascii(b"f32"); +pub const F64: Name = Name::new_inline_ascii(b"f64"); +pub const BOOL: Name = Name::new_inline_ascii(b"bool"); +pub const CHAR: Name = Name::new_inline_ascii(b"char"); +pub const STR: Name = Name::new_inline_ascii(b"str"); // Special names -pub const SELF_PARAM: Name = Name::new_inline_ascii(4, b"self"); -pub const SELF_TYPE: Name = Name::new_inline_ascii(4, b"Self"); -pub const MACRO_RULES: Name = Name::new_inline_ascii(11, b"macro_rules"); +pub const SELF_PARAM: Name = Name::new_inline_ascii(b"self"); +pub const SELF_TYPE: Name = Name::new_inline_ascii(b"Self"); +pub const MACRO_RULES: Name = Name::new_inline_ascii(b"macro_rules"); // Components of known path (value or mod name) -pub const STD: Name = Name::new_inline_ascii(3, b"std"); -pub const ITER: Name = Name::new_inline_ascii(4, b"iter"); -pub const OPS: Name = Name::new_inline_ascii(3, b"ops"); -pub const FUTURE: Name = Name::new_inline_ascii(6, b"future"); -pub const RESULT: Name = Name::new_inline_ascii(6, b"result"); -pub const BOXED: Name = Name::new_inline_ascii(5, b"boxed"); +pub const STD: Name = Name::new_inline_ascii(b"std"); +pub const ITER: Name = Name::new_inline_ascii(b"iter"); +pub const OPS: Name = Name::new_inline_ascii(b"ops"); +pub const FUTURE: Name = Name::new_inline_ascii(b"future"); +pub const RESULT: Name = Name::new_inline_ascii(b"result"); +pub const BOXED: Name = Name::new_inline_ascii(b"boxed"); // Components of known path (type name) -pub const INTO_ITERATOR_TYPE: Name = Name::new_inline_ascii(12, b"IntoIterator"); -pub const ITEM_TYPE: Name = Name::new_inline_ascii(4, b"Item"); -pub const TRY_TYPE: Name = Name::new_inline_ascii(3, b"Try"); -pub const OK_TYPE: Name = Name::new_inline_ascii(2, b"Ok"); -pub const FUTURE_TYPE: Name = Name::new_inline_ascii(6, b"Future"); -pub const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result"); -pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output"); -pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target"); -pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box"); -pub const RANGE_FROM_TYPE: Name = Name::new_inline_ascii(9, b"RangeFrom"); -pub const RANGE_FULL_TYPE: Name = Name::new_inline_ascii(9, b"RangeFull"); -pub const RANGE_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(14, b"RangeInclusive"); -pub const RANGE_TO_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(16, b"RangeToInclusive"); -pub const RANGE_TO_TYPE: Name = Name::new_inline_ascii(7, b"RangeTo"); -pub const RANGE_TYPE: Name = Name::new_inline_ascii(5, b"Range"); +pub const INTO_ITERATOR_TYPE: Name = Name::new_inline_ascii(b"IntoIterator"); +pub const ITEM_TYPE: Name = Name::new_inline_ascii(b"Item"); +pub const TRY_TYPE: Name = Name::new_inline_ascii(b"Try"); +pub const OK_TYPE: Name = Name::new_inline_ascii(b"Ok"); +pub const FUTURE_TYPE: Name = Name::new_inline_ascii(b"Future"); +pub const RESULT_TYPE: Name = Name::new_inline_ascii(b"Result"); +pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(b"Output"); +pub const TARGET_TYPE: Name = Name::new_inline_ascii(b"Target"); +pub const BOX_TYPE: Name = Name::new_inline_ascii(b"Box"); +pub const RANGE_FROM_TYPE: Name = Name::new_inline_ascii(b"RangeFrom"); +pub const RANGE_FULL_TYPE: Name = Name::new_inline_ascii(b"RangeFull"); +pub const RANGE_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(b"RangeInclusive"); +pub const RANGE_TO_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(b"RangeToInclusive"); +pub const RANGE_TO_TYPE: Name = Name::new_inline_ascii(b"RangeTo"); +pub const RANGE_TYPE: Name = Name::new_inline_ascii(b"Range"); // Builtin Macros -pub const FILE_MACRO: Name = Name::new_inline_ascii(4, b"file"); -pub const COLUMN_MACRO: Name = Name::new_inline_ascii(6, b"column"); -pub const COMPILE_ERROR_MACRO: Name = Name::new_inline_ascii(13, b"compile_error"); -pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line"); -pub const STRINGIFY_MACRO: Name = Name::new_inline_ascii(9, b"stringify"); -pub const FORMAT_ARGS_MACRO: Name = Name::new_inline_ascii(11, b"format_args"); -pub const FORMAT_ARGS_NL_MACRO: Name = Name::new_inline_ascii(14, b"format_args_nl"); +pub const FILE_MACRO: Name = Name::new_inline_ascii(b"file"); +pub const COLUMN_MACRO: Name = Name::new_inline_ascii(b"column"); +pub const COMPILE_ERROR_MACRO: Name = Name::new_inline_ascii(b"compile_error"); +pub const LINE_MACRO: Name = Name::new_inline_ascii(b"line"); +pub const STRINGIFY_MACRO: Name = Name::new_inline_ascii(b"stringify"); +pub const FORMAT_ARGS_MACRO: Name = Name::new_inline_ascii(b"format_args"); +pub const FORMAT_ARGS_NL_MACRO: Name = Name::new_inline_ascii(b"format_args_nl"); // Builtin derives -pub const COPY_TRAIT: Name = Name::new_inline_ascii(4, b"Copy"); -pub const CLONE_TRAIT: Name = Name::new_inline_ascii(5, b"Clone"); -pub const DEFAULT_TRAIT: Name = Name::new_inline_ascii(7, b"Default"); -pub const DEBUG_TRAIT: Name = Name::new_inline_ascii(5, b"Debug"); -pub const HASH_TRAIT: Name = Name::new_inline_ascii(4, b"Hash"); -pub const ORD_TRAIT: Name = Name::new_inline_ascii(3, b"Ord"); -pub const PARTIAL_ORD_TRAIT: Name = Name::new_inline_ascii(10, b"PartialOrd"); -pub const EQ_TRAIT: Name = Name::new_inline_ascii(2, b"Eq"); -pub const PARTIAL_EQ_TRAIT: Name = Name::new_inline_ascii(9, b"PartialEq"); +pub const COPY_TRAIT: Name = Name::new_inline_ascii(b"Copy"); +pub const CLONE_TRAIT: Name = Name::new_inline_ascii(b"Clone"); +pub const DEFAULT_TRAIT: Name = Name::new_inline_ascii(b"Default"); +pub const DEBUG_TRAIT: Name = Name::new_inline_ascii(b"Debug"); +pub const HASH_TRAIT: Name = Name::new_inline_ascii(b"Hash"); +pub const ORD_TRAIT: Name = Name::new_inline_ascii(b"Ord"); +pub const PARTIAL_ORD_TRAIT: Name = Name::new_inline_ascii(b"PartialOrd"); +pub const EQ_TRAIT: Name = Name::new_inline_ascii(b"Eq"); +pub const PARTIAL_EQ_TRAIT: Name = Name::new_inline_ascii(b"PartialEq"); -- cgit v1.2.3 From eae425b10fd7803ae67d2d39e9aca902daa353ba Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 6 Dec 2019 19:30:01 +0100 Subject: Implement format_args more properly --- crates/ra_hir_expand/src/builtin_macro.rs | 47 ++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 4 deletions(-) diff --git a/crates/ra_hir_expand/src/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index e0709704a..99303188b 100644 --- a/crates/ra_hir_expand/src/builtin_macro.rs +++ b/crates/ra_hir_expand/src/builtin_macro.rs @@ -207,12 +207,34 @@ fn compile_error_expand( fn format_args_expand( _db: &dyn AstDatabase, _id: MacroCallId, - _tt: &tt::Subtree, + tt: &tt::Subtree, ) -> Result { - // FIXME this is just a stub to make format macros type-check without mismatches - // We should make this at least insert the arguments, so that go to def etc. work within format macros + // We expand `format_args!("", arg1, arg2)` to + // `std::fmt::Arguments::new_v1(&[], &[&arg1, &arg2])`, + // which is still not really correct, but close enough for now + let mut args = Vec::new(); + let mut current = Vec::new(); + for tt in tt.token_trees.iter().cloned() { + match tt { + tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => { + args.push(tt::Subtree { delimiter: tt::Delimiter::None, token_trees: current }); + current = Vec::new(); + } + _ => { + current.push(tt); + } + } + } + if !current.is_empty() { + args.push(tt::Subtree { delimiter: tt::Delimiter::None, token_trees: current }); + } + if args.is_empty() { + return Err(mbe::ExpandError::NoMatchingRule); + } + let _format_string = args.remove(0); + let arg_tts = args.into_iter().flat_map(|arg| (quote! { & #arg , }).token_trees); let expanded = quote! { - std::fmt::Arguments::new_v1(&[], &[]) + std::fmt::Arguments::new_v1(&[], &[##arg_tts]) }; Ok(expanded) } @@ -324,4 +346,21 @@ mod tests { assert_eq!(expanded, r#"loop{"error!"}"#); } + + #[test] + fn test_format_args_expand() { + let expanded = expand_builtin_macro( + r#" + #[rustc_builtin_macro] + macro_rules! format_args { + ($fmt:expr) => ({ /* compiler built-in */ }); + ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) + } + format_args!("{} {:?}", arg1(a, b, c), arg2); +"#, + BuiltinFnLikeExpander::FormatArgs, + ); + + assert_eq!(expanded, r#"std::fmt::Arguments::new_v1(&[] ,&[&arg1(a,b,c),&arg2,])"#); + } } -- cgit v1.2.3 From a565072ddeac519eea3b415a57e9fd208e20e562 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 6 Dec 2019 19:30:15 +0100 Subject: Try to make go to definition work in format! SourceAnalyzer didn't work properly within expression macro expansions because it didn't find the enclosing function. Fix this by going up the expansion chain to find ancestors. This makes the test work, but apparently in real usage it's still not working. --- crates/ra_hir/src/source_binder.rs | 23 ++++++++++++++++++----- crates/ra_hir_expand/src/lib.rs | 18 ++++++++++++++++++ crates/ra_ide/src/goto_definition.rs | 27 +++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 5 deletions(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 42c392513..cb4345ca1 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -76,18 +76,30 @@ fn def_with_body_from_child_node( db: &impl HirDatabase, child: InFile<&SyntaxNode>, ) -> Option { - child.value.ancestors().find_map(|node| { + ancestors_with_macros(db, child).find_map(|node| { + let n = &node.value; match_ast! { - match node { - ast::FnDef(def) => { return Function::from_source(db, child.with_value(def)).map(DefWithBody::from); }, - ast::ConstDef(def) => { return Const::from_source(db, child.with_value(def)).map(DefWithBody::from); }, - ast::StaticDef(def) => { return Static::from_source(db, child.with_value(def)).map(DefWithBody::from); }, + match n { + ast::FnDef(def) => { return Function::from_source(db, node.with_value(def)).map(DefWithBody::from); }, + ast::ConstDef(def) => { return Const::from_source(db, node.with_value(def)).map(DefWithBody::from); }, + ast::StaticDef(def) => { return Static::from_source(db, node.with_value(def)).map(DefWithBody::from); }, _ => { None }, } } }) } +fn ancestors_with_macros<'a>( + db: &'a (impl HirDatabase), + node: InFile<&SyntaxNode>, +) -> impl Iterator> + 'a { + let file = node.with_value(()); // keep just the file id for borrow checker purposes + let parent_node = node.file_id.call_node(db); + let parent_ancestors: Box>> = + Box::new(parent_node.into_iter().flat_map(move |n| ancestors_with_macros(db, n.as_ref()))); + node.value.ancestors().map(move |n| file.with_value(n)).chain(parent_ancestors) +} + /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of /// original source files. It should not be used inside the HIR itself. #[derive(Debug)] @@ -135,6 +147,7 @@ pub struct ReferenceDescriptor { pub name: String, } +#[derive(Debug)] pub struct Expansion { macro_file_kind: MacroFileKind, macro_call_id: MacroCallId, diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 59c69b91b..0c1dc87e6 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs @@ -76,6 +76,17 @@ impl HirFileId { } } + /// If this is a macro call, returns the syntax node of the call. + pub fn call_node(self, db: &dyn db::AstDatabase) -> Option> { + match self.0 { + HirFileIdRepr::FileId(_) => None, + HirFileIdRepr::MacroFile(macro_file) => { + let loc = db.lookup_intern_macro(macro_file.macro_call_id); + Some(loc.kind.node(db)) + } + } + } + /// Return expansion information if it is a macro-expansion file pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option { match self.0 { @@ -176,6 +187,13 @@ impl MacroCallKind { } } + pub fn node(&self, db: &dyn db::AstDatabase) -> InFile { + match self { + MacroCallKind::FnLike(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()), + MacroCallKind::Attr(ast_id) => ast_id.with_value(ast_id.to_node(db).syntax().clone()), + } + } + pub fn arg(&self, db: &dyn db::AstDatabase) -> Option { match self { MacroCallKind::FnLike(ast_id) => { diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 76a741207..b1d567ca7 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -693,4 +693,31 @@ mod tests { "foo FN_DEF FileId(1) [52; 63) [55; 58)", ); } + + #[test] + fn goto_through_format() { + check_goto( + " + //- /lib.rs + #[macro_export] + macro_rules! format { + ($($arg:tt)*) => ($crate::fmt::format($crate::__export::format_args!($($arg)*))) + } + #[rustc_builtin_macro] + #[macro_export] + macro_rules! format_args { + ($fmt:expr) => ({ /* compiler built-in */ }); + ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ }) + } + pub mod __export { + pub use crate::format_args; + } + fn foo() -> i8 {} + fn test() { + format!(\"{}\", fo<|>o()) + } + ", + "foo FN_DEF FileId(1) [359; 376) [362; 365)", + ); + } } -- cgit v1.2.3 From 4a99ef5c39e670162ea3c35d53ccd75b81281865 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 6 Dec 2019 21:21:39 +0100 Subject: Builtin macros only use caller tokens --- crates/ra_hir_expand/src/db.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 { pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) { match self { TokenExpander::MacroRules(it) => it.map_id_up(id), - TokenExpander::Builtin(..) => (id, mbe::Origin::Def), - TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Def), + TokenExpander::Builtin(..) => (id, mbe::Origin::Call), + TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call), } } } -- cgit v1.2.3 From c80dc0ad3aee717f9d15c11d300d0eb1c10f1cc8 Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 6 Dec 2019 21:24:41 +0100 Subject: Make the goto_through_format test actually fail :( --- crates/ra_ide/src/goto_definition.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index b1d567ca7..d3c198813 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -689,11 +689,13 @@ mod tests { fo<|>o(); } } + mod confuse_index { fn foo(); } ", "foo FN_DEF FileId(1) [52; 63) [55; 58)", ); } + #[should_panic] // currently failing because of expr mapping problems #[test] fn goto_through_format() { check_goto( @@ -711,6 +713,7 @@ mod tests { } pub mod __export { pub use crate::format_args; + fn foo() {} // for index confusion } fn foo() -> i8 {} fn test() { -- cgit v1.2.3 From b2c01f446edcbc12b5dd870064cbfc6c1a47eb8b Mon Sep 17 00:00:00 2001 From: Florian Diebold Date: Fri, 6 Dec 2019 21:46:18 +0100 Subject: Implement ancestors_with_macros in a better way --- crates/ra_hir/src/source_binder.rs | 13 +------------ crates/ra_hir_expand/src/lib.rs | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index cb4345ca1..0e136b904 100644 --- a/crates/ra_hir/src/source_binder.rs +++ b/crates/ra_hir/src/source_binder.rs @@ -76,7 +76,7 @@ fn def_with_body_from_child_node( db: &impl HirDatabase, child: InFile<&SyntaxNode>, ) -> Option { - ancestors_with_macros(db, child).find_map(|node| { + child.cloned().ancestors_with_macros(db).find_map(|node| { let n = &node.value; match_ast! { match n { @@ -89,17 +89,6 @@ fn def_with_body_from_child_node( }) } -fn ancestors_with_macros<'a>( - db: &'a (impl HirDatabase), - node: InFile<&SyntaxNode>, -) -> impl Iterator> + 'a { - let file = node.with_value(()); // keep just the file id for borrow checker purposes - let parent_node = node.file_id.call_node(db); - let parent_ancestors: Box>> = - Box::new(parent_node.into_iter().flat_map(move |n| ancestors_with_macros(db, n.as_ref()))); - node.value.ancestors().map(move |n| file.with_value(n)).chain(parent_ancestors) -} - /// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of /// original source files. It should not be used inside the HIR itself. #[derive(Debug)] diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 0c1dc87e6..0a5da7e54 100644 --- a/crates/ra_hir_expand/src/lib.rs +++ b/crates/ra_hir_expand/src/lib.rs @@ -301,3 +301,24 @@ impl InFile { db.parse_or_expand(self.file_id).expect("source created from invalid file") } } + +impl InFile<&T> { + pub fn cloned(&self) -> InFile { + self.with_value(self.value.clone()) + } +} + +impl InFile { + pub fn ancestors_with_macros<'a>( + self, + db: &'a impl crate::db::AstDatabase, + ) -> impl Iterator> + 'a { + std::iter::successors(Some(self), move |node| match node.value.parent() { + Some(parent) => Some(node.with_value(parent)), + None => { + let parent_node = node.file_id.call_node(db)?; + Some(parent_node) + } + }) + } +} -- cgit v1.2.3