diff --git a/crates/ra_hir/src/source_binder.rs b/crates/ra_hir/src/source_binder.rs index 42c3925135d..0e136b90498 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( db: &impl HirDatabase, child: InFile<&SyntaxNode>, ) -> Option { - child.value.ancestors().find_map(|node| { + child.cloned().ancestors_with_macros(db).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 }, } } @@ -135,6 +136,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/builtin_macro.rs b/crates/ra_hir_expand/src/builtin_macro.rs index 35f99b2bc4c..99303188bf2 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,41 @@ 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 { + // 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(&[], &[##arg_tts]) + }; + Ok(expanded) +} + #[cfg(test)] mod tests { use super::*; @@ -307,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,])"#); + } } diff --git a/crates/ra_hir_expand/src/db.rs b/crates/ra_hir_expand/src/db.rs index 99dabf3fbb5..013a6c8ba95 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), } } } diff --git a/crates/ra_hir_expand/src/lib.rs b/crates/ra_hir_expand/src/lib.rs index 59c69b91bb2..0a5da7e5479 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) => { @@ -283,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) + } + }) + } +} diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs index c5a19116093..4f2f702c01f 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,68 +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 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"); diff --git a/crates/ra_ide/src/goto_definition.rs b/crates/ra_ide/src/goto_definition.rs index 76a74120762..d3c1988132e 100644 --- a/crates/ra_ide/src/goto_definition.rs +++ b/crates/ra_ide/src/goto_definition.rs @@ -689,8 +689,38 @@ 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( + " + //- /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() {} // for index confusion + } + fn foo() -> i8 {} + fn test() { + format!(\"{}\", fo<|>o()) + } + ", + "foo FN_DEF FileId(1) [359; 376) [362; 365)", + ); + } }