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 <flodiebold@gmail.com>
This commit is contained in:
commit
431836f4a0
@ -76,12 +76,13 @@ fn def_with_body_from_child_node(
|
||||
db: &impl HirDatabase,
|
||||
child: InFile<&SyntaxNode>,
|
||||
) -> Option<DefWithBody> {
|
||||
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,
|
||||
|
@ -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<tt::Subtree, mbe::ExpandError> {
|
||||
// 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,])"#);
|
||||
}
|
||||
}
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<InFile<SyntaxNode>> {
|
||||
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<ExpansionInfo> {
|
||||
match self.0 {
|
||||
@ -176,6 +187,13 @@ impl MacroCallKind {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn node(&self, db: &dyn db::AstDatabase) -> InFile<SyntaxNode> {
|
||||
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<SyntaxNode> {
|
||||
match self {
|
||||
MacroCallKind::FnLike(ast_id) => {
|
||||
@ -283,3 +301,24 @@ impl<T> InFile<T> {
|
||||
db.parse_or_expand(self.file_id).expect("source created from invalid file")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> InFile<&T> {
|
||||
pub fn cloned(&self) -> InFile<T> {
|
||||
self.with_value(self.value.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl InFile<SyntaxNode> {
|
||||
pub fn ancestors_with_macros<'a>(
|
||||
self,
|
||||
db: &'a impl crate::db::AstDatabase,
|
||||
) -> impl Iterator<Item = InFile<SyntaxNode>> + '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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
|
@ -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)",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user