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(-) (limited to 'crates/ra_hir_expand/src') 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(-) (limited to 'crates/ra_hir_expand/src') 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(-) (limited to 'crates/ra_hir_expand/src') 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_expand/src/lib.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'crates/ra_hir_expand/src') 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) => { -- 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(-) (limited to 'crates/ra_hir_expand/src') 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 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_expand/src/lib.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'crates/ra_hir_expand/src') 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