Skip to content

Commit 431836f

Browse files
Merge #2489
2489: Implement `format_args` r=flodiebold a=flodiebold This fixes a huge amount of type mismatches (because every format call was a type mismatch so far); I also hoped to get go to def working within `format!` etc., and the test says it should, but in practice it still doesn't seem to... Also remove the `len` parameter from `Name::new_inline_ascii`, which I'm assuming was only there because of `const fn` limitations? cc @edwin0cheng Co-authored-by: Florian Diebold <[email protected]>
2 parents f18b7e1 + b2c01f4 commit 431836f

File tree

6 files changed

+194
-65
lines changed

6 files changed

+194
-65
lines changed

crates/ra_hir/src/source_binder.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,13 @@ fn def_with_body_from_child_node(
7676
db: &impl HirDatabase,
7777
child: InFile<&SyntaxNode>,
7878
) -> Option<DefWithBody> {
79-
child.value.ancestors().find_map(|node| {
79+
child.cloned().ancestors_with_macros(db).find_map(|node| {
80+
let n = &node.value;
8081
match_ast! {
81-
match node {
82-
ast::FnDef(def) => { return Function::from_source(db, child.with_value(def)).map(DefWithBody::from); },
83-
ast::ConstDef(def) => { return Const::from_source(db, child.with_value(def)).map(DefWithBody::from); },
84-
ast::StaticDef(def) => { return Static::from_source(db, child.with_value(def)).map(DefWithBody::from); },
82+
match n {
83+
ast::FnDef(def) => { return Function::from_source(db, node.with_value(def)).map(DefWithBody::from); },
84+
ast::ConstDef(def) => { return Const::from_source(db, node.with_value(def)).map(DefWithBody::from); },
85+
ast::StaticDef(def) => { return Static::from_source(db, node.with_value(def)).map(DefWithBody::from); },
8586
_ => { None },
8687
}
8788
}
@@ -135,6 +136,7 @@ pub struct ReferenceDescriptor {
135136
pub name: String,
136137
}
137138

139+
#[derive(Debug)]
138140
pub struct Expansion {
139141
macro_file_kind: MacroFileKind,
140142
macro_call_id: MacroCallId,

crates/ra_hir_expand/src/builtin_macro.rs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,11 @@ register_builtin! {
4949
(COMPILE_ERROR_MACRO, CompileError) => compile_error_expand,
5050
(FILE_MACRO, File) => file_expand,
5151
(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
5357
}
5458

5559
fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
@@ -200,6 +204,41 @@ fn compile_error_expand(
200204
Err(mbe::ExpandError::BindingError("Must be a string".into()))
201205
}
202206

207+
fn 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+
203242
#[cfg(test)]
204243
mod tests {
205244
use super::*;
@@ -307,4 +346,21 @@ mod tests {
307346

308347
assert_eq!(expanded, r#"loop{"error!"}"#);
309348
}
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+
}
310366
}

crates/ra_hir_expand/src/db.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ impl TokenExpander {
4545
pub fn map_id_up(&self, id: tt::TokenId) -> (tt::TokenId, mbe::Origin) {
4646
match self {
4747
TokenExpander::MacroRules(it) => it.map_id_up(id),
48-
TokenExpander::Builtin(..) => (id, mbe::Origin::Def),
49-
TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Def),
48+
TokenExpander::Builtin(..) => (id, mbe::Origin::Call),
49+
TokenExpander::BuiltinDerive(..) => (id, mbe::Origin::Call),
5050
}
5151
}
5252
}

crates/ra_hir_expand/src/lib.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,17 @@ impl HirFileId {
7676
}
7777
}
7878

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+
7990
/// Return expansion information if it is a macro-expansion file
8091
pub fn expansion_info(self, db: &dyn db::AstDatabase) -> Option<ExpansionInfo> {
8192
match self.0 {
@@ -176,6 +187,13 @@ impl MacroCallKind {
176187
}
177188
}
178189

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+
179197
pub fn arg(&self, db: &dyn db::AstDatabase) -> Option<SyntaxNode> {
180198
match self {
181199
MacroCallKind::FnLike(ast_id) => {
@@ -283,3 +301,24 @@ impl<T> InFile<T> {
283301
db.parse_or_expand(self.file_id).expect("source created from invalid file")
284302
}
285303
}
304+
305+
impl<T: Clone> InFile<&T> {
306+
pub fn cloned(&self) -> InFile<T> {
307+
self.with_value(self.value.clone())
308+
}
309+
}
310+
311+
impl 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+
}

crates/ra_hir_expand/src/name.rs

Lines changed: 59 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ impl Name {
3838
}
3939

4040
/// Shortcut to create inline plain text name
41-
const fn new_inline_ascii(len: usize, text: &[u8]) -> Name {
42-
Name::new_text(SmolStr::new_inline_from_ascii(len, text))
41+
const fn new_inline_ascii(text: &[u8]) -> Name {
42+
Name::new_text(SmolStr::new_inline_from_ascii(text.len(), text))
4343
}
4444

4545
/// Resolve a name from the text of token.
@@ -105,68 +105,70 @@ impl AsName for ra_db::Dependency {
105105
}
106106

107107
// Primitives
108-
pub const ISIZE: Name = Name::new_inline_ascii(5, b"isize");
109-
pub const I8: Name = Name::new_inline_ascii(2, b"i8");
110-
pub const I16: Name = Name::new_inline_ascii(3, b"i16");
111-
pub const I32: Name = Name::new_inline_ascii(3, b"i32");
112-
pub const I64: Name = Name::new_inline_ascii(3, b"i64");
113-
pub const I128: Name = Name::new_inline_ascii(4, b"i128");
114-
pub const USIZE: Name = Name::new_inline_ascii(5, b"usize");
115-
pub const U8: Name = Name::new_inline_ascii(2, b"u8");
116-
pub const U16: Name = Name::new_inline_ascii(3, b"u16");
117-
pub const U32: Name = Name::new_inline_ascii(3, b"u32");
118-
pub const U64: Name = Name::new_inline_ascii(3, b"u64");
119-
pub const U128: Name = Name::new_inline_ascii(4, b"u128");
120-
pub const F32: Name = Name::new_inline_ascii(3, b"f32");
121-
pub const F64: Name = Name::new_inline_ascii(3, b"f64");
122-
pub const BOOL: Name = Name::new_inline_ascii(4, b"bool");
123-
pub const CHAR: Name = Name::new_inline_ascii(4, b"char");
124-
pub const STR: Name = Name::new_inline_ascii(3, b"str");
108+
pub const ISIZE: Name = Name::new_inline_ascii(b"isize");
109+
pub const I8: Name = Name::new_inline_ascii(b"i8");
110+
pub const I16: Name = Name::new_inline_ascii(b"i16");
111+
pub const I32: Name = Name::new_inline_ascii(b"i32");
112+
pub const I64: Name = Name::new_inline_ascii(b"i64");
113+
pub const I128: Name = Name::new_inline_ascii(b"i128");
114+
pub const USIZE: Name = Name::new_inline_ascii(b"usize");
115+
pub const U8: Name = Name::new_inline_ascii(b"u8");
116+
pub const U16: Name = Name::new_inline_ascii(b"u16");
117+
pub const U32: Name = Name::new_inline_ascii(b"u32");
118+
pub const U64: Name = Name::new_inline_ascii(b"u64");
119+
pub const U128: Name = Name::new_inline_ascii(b"u128");
120+
pub const F32: Name = Name::new_inline_ascii(b"f32");
121+
pub const F64: Name = Name::new_inline_ascii(b"f64");
122+
pub const BOOL: Name = Name::new_inline_ascii(b"bool");
123+
pub const CHAR: Name = Name::new_inline_ascii(b"char");
124+
pub const STR: Name = Name::new_inline_ascii(b"str");
125125

126126
// Special names
127-
pub const SELF_PARAM: Name = Name::new_inline_ascii(4, b"self");
128-
pub const SELF_TYPE: Name = Name::new_inline_ascii(4, b"Self");
129-
pub const MACRO_RULES: Name = Name::new_inline_ascii(11, b"macro_rules");
127+
pub const SELF_PARAM: Name = Name::new_inline_ascii(b"self");
128+
pub const SELF_TYPE: Name = Name::new_inline_ascii(b"Self");
129+
pub const MACRO_RULES: Name = Name::new_inline_ascii(b"macro_rules");
130130

131131
// Components of known path (value or mod name)
132-
pub const STD: Name = Name::new_inline_ascii(3, b"std");
133-
pub const ITER: Name = Name::new_inline_ascii(4, b"iter");
134-
pub const OPS: Name = Name::new_inline_ascii(3, b"ops");
135-
pub const FUTURE: Name = Name::new_inline_ascii(6, b"future");
136-
pub const RESULT: Name = Name::new_inline_ascii(6, b"result");
137-
pub const BOXED: Name = Name::new_inline_ascii(5, b"boxed");
132+
pub const STD: Name = Name::new_inline_ascii(b"std");
133+
pub const ITER: Name = Name::new_inline_ascii(b"iter");
134+
pub const OPS: Name = Name::new_inline_ascii(b"ops");
135+
pub const FUTURE: Name = Name::new_inline_ascii(b"future");
136+
pub const RESULT: Name = Name::new_inline_ascii(b"result");
137+
pub const BOXED: Name = Name::new_inline_ascii(b"boxed");
138138

139139
// Components of known path (type name)
140-
pub const INTO_ITERATOR_TYPE: Name = Name::new_inline_ascii(12, b"IntoIterator");
141-
pub const ITEM_TYPE: Name = Name::new_inline_ascii(4, b"Item");
142-
pub const TRY_TYPE: Name = Name::new_inline_ascii(3, b"Try");
143-
pub const OK_TYPE: Name = Name::new_inline_ascii(2, b"Ok");
144-
pub const FUTURE_TYPE: Name = Name::new_inline_ascii(6, b"Future");
145-
pub const RESULT_TYPE: Name = Name::new_inline_ascii(6, b"Result");
146-
pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(6, b"Output");
147-
pub const TARGET_TYPE: Name = Name::new_inline_ascii(6, b"Target");
148-
pub const BOX_TYPE: Name = Name::new_inline_ascii(3, b"Box");
149-
pub const RANGE_FROM_TYPE: Name = Name::new_inline_ascii(9, b"RangeFrom");
150-
pub const RANGE_FULL_TYPE: Name = Name::new_inline_ascii(9, b"RangeFull");
151-
pub const RANGE_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(14, b"RangeInclusive");
152-
pub const RANGE_TO_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(16, b"RangeToInclusive");
153-
pub const RANGE_TO_TYPE: Name = Name::new_inline_ascii(7, b"RangeTo");
154-
pub const RANGE_TYPE: Name = Name::new_inline_ascii(5, b"Range");
140+
pub const INTO_ITERATOR_TYPE: Name = Name::new_inline_ascii(b"IntoIterator");
141+
pub const ITEM_TYPE: Name = Name::new_inline_ascii(b"Item");
142+
pub const TRY_TYPE: Name = Name::new_inline_ascii(b"Try");
143+
pub const OK_TYPE: Name = Name::new_inline_ascii(b"Ok");
144+
pub const FUTURE_TYPE: Name = Name::new_inline_ascii(b"Future");
145+
pub const RESULT_TYPE: Name = Name::new_inline_ascii(b"Result");
146+
pub const OUTPUT_TYPE: Name = Name::new_inline_ascii(b"Output");
147+
pub const TARGET_TYPE: Name = Name::new_inline_ascii(b"Target");
148+
pub const BOX_TYPE: Name = Name::new_inline_ascii(b"Box");
149+
pub const RANGE_FROM_TYPE: Name = Name::new_inline_ascii(b"RangeFrom");
150+
pub const RANGE_FULL_TYPE: Name = Name::new_inline_ascii(b"RangeFull");
151+
pub const RANGE_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(b"RangeInclusive");
152+
pub const RANGE_TO_INCLUSIVE_TYPE: Name = Name::new_inline_ascii(b"RangeToInclusive");
153+
pub const RANGE_TO_TYPE: Name = Name::new_inline_ascii(b"RangeTo");
154+
pub const RANGE_TYPE: Name = Name::new_inline_ascii(b"Range");
155155

156156
// Builtin Macros
157-
pub const FILE_MACRO: Name = Name::new_inline_ascii(4, b"file");
158-
pub const COLUMN_MACRO: Name = Name::new_inline_ascii(6, b"column");
159-
pub const COMPILE_ERROR_MACRO: Name = Name::new_inline_ascii(13, b"compile_error");
160-
pub const LINE_MACRO: Name = Name::new_inline_ascii(4, b"line");
161-
pub const STRINGIFY_MACRO: Name = Name::new_inline_ascii(9, b"stringify");
157+
pub const FILE_MACRO: Name = Name::new_inline_ascii(b"file");
158+
pub const COLUMN_MACRO: Name = Name::new_inline_ascii(b"column");
159+
pub const COMPILE_ERROR_MACRO: Name = Name::new_inline_ascii(b"compile_error");
160+
pub const LINE_MACRO: Name = Name::new_inline_ascii(b"line");
161+
pub const STRINGIFY_MACRO: Name = Name::new_inline_ascii(b"stringify");
162+
pub const FORMAT_ARGS_MACRO: Name = Name::new_inline_ascii(b"format_args");
163+
pub const FORMAT_ARGS_NL_MACRO: Name = Name::new_inline_ascii(b"format_args_nl");
162164

163165
// Builtin derives
164-
pub const COPY_TRAIT: Name = Name::new_inline_ascii(4, b"Copy");
165-
pub const CLONE_TRAIT: Name = Name::new_inline_ascii(5, b"Clone");
166-
pub const DEFAULT_TRAIT: Name = Name::new_inline_ascii(7, b"Default");
167-
pub const DEBUG_TRAIT: Name = Name::new_inline_ascii(5, b"Debug");
168-
pub const HASH_TRAIT: Name = Name::new_inline_ascii(4, b"Hash");
169-
pub const ORD_TRAIT: Name = Name::new_inline_ascii(3, b"Ord");
170-
pub const PARTIAL_ORD_TRAIT: Name = Name::new_inline_ascii(10, b"PartialOrd");
171-
pub const EQ_TRAIT: Name = Name::new_inline_ascii(2, b"Eq");
172-
pub const PARTIAL_EQ_TRAIT: Name = Name::new_inline_ascii(9, b"PartialEq");
166+
pub const COPY_TRAIT: Name = Name::new_inline_ascii(b"Copy");
167+
pub const CLONE_TRAIT: Name = Name::new_inline_ascii(b"Clone");
168+
pub const DEFAULT_TRAIT: Name = Name::new_inline_ascii(b"Default");
169+
pub const DEBUG_TRAIT: Name = Name::new_inline_ascii(b"Debug");
170+
pub const HASH_TRAIT: Name = Name::new_inline_ascii(b"Hash");
171+
pub const ORD_TRAIT: Name = Name::new_inline_ascii(b"Ord");
172+
pub const PARTIAL_ORD_TRAIT: Name = Name::new_inline_ascii(b"PartialOrd");
173+
pub const EQ_TRAIT: Name = Name::new_inline_ascii(b"Eq");
174+
pub const PARTIAL_EQ_TRAIT: Name = Name::new_inline_ascii(b"PartialEq");

crates/ra_ide/src/goto_definition.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -689,8 +689,38 @@ mod tests {
689689
fo<|>o();
690690
}
691691
}
692+
mod confuse_index { fn foo(); }
692693
",
693694
"foo FN_DEF FileId(1) [52; 63) [55; 58)",
694695
);
695696
}
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+
}
696726
}

0 commit comments

Comments
 (0)