Make stringify! prettify its input

This will insert whitespace if the invocation is inside another macro
This commit is contained in:
Jonas Schievink 2021-09-27 19:02:03 +02:00
parent f22eea9053
commit d05eae6ada
6 changed files with 48 additions and 133 deletions

View file

@ -1,7 +1,7 @@
//! Builtin macro //! Builtin macro
use crate::{ use crate::{
db::AstDatabase, name, quote, AstId, CrateId, MacroCallId, MacroCallLoc, MacroDefId, db::AstDatabase, name, quote, AstId, CrateId, MacroCallId, MacroCallLoc, MacroDefId,
MacroDefKind, TextSize, MacroDefKind,
}; };
use base_db::{AnchoredPath, Edition, FileId}; use base_db::{AnchoredPath, Edition, FileId};
@ -148,25 +148,14 @@ fn line_expand(
} }
fn stringify_expand( fn stringify_expand(
db: &dyn AstDatabase, _db: &dyn AstDatabase,
id: MacroCallId, _id: MacroCallId,
_tt: &tt::Subtree, tt: &tt::Subtree,
) -> ExpandResult<tt::Subtree> { ) -> ExpandResult<tt::Subtree> {
let loc = db.lookup_intern_macro(id); let pretty = tt::pretty(&tt.token_trees);
let macro_content = {
let arg = match loc.kind.arg(db) {
Some(arg) => arg,
None => return ExpandResult::only_err(mbe::ExpandError::UnexpectedToken),
};
let macro_args = arg;
let text = macro_args.text();
let without_parens = TextSize::of('(')..text.len() - TextSize::of(')');
text.slice(without_parens).to_string()
};
let expanded = quote! { let expanded = quote! {
#macro_content #pretty
}; };
ExpandResult::ok(expanded) ExpandResult::ok(expanded)
@ -685,7 +674,11 @@ mod tests {
r#" r#"
#[rustc_builtin_macro] #[rustc_builtin_macro]
macro_rules! stringify {() => {}} macro_rules! stringify {() => {}}
stringify!(a b c) stringify!(
a
b
c
)
"#, "#,
expect![["\"a b c\""]], expect![["\"a b c\""]],
); );

View file

@ -26,7 +26,7 @@ use base_db::{impl_intern_key, salsa, CrateId, FileId, FileRange};
use syntax::{ use syntax::{
algo::skip_trivia_token, algo::skip_trivia_token,
ast::{self, AstNode, HasAttrs}, ast::{self, AstNode, HasAttrs},
Direction, SyntaxNode, SyntaxToken, TextRange, TextSize, Direction, SyntaxNode, SyntaxToken, TextRange,
}; };
use crate::{ use crate::{

View file

@ -199,44 +199,7 @@ pub mod token_stream {
impl ToString for TokenStream { impl ToString for TokenStream {
fn to_string(&self) -> String { fn to_string(&self) -> String {
return tokentrees_to_text(&self.token_trees[..]); tt::pretty(&self.token_trees)
fn tokentrees_to_text(tkns: &[tt::TokenTree]) -> String {
tkns.iter()
.fold((String::new(), true), |(last, last_to_joint), tkn| {
let s = [last, tokentree_to_text(tkn)].join(if last_to_joint {
""
} else {
" "
});
let mut is_joint = false;
if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn {
if punct.spacing == tt::Spacing::Joint {
is_joint = true;
}
}
(s, is_joint)
})
.0
}
fn tokentree_to_text(tkn: &tt::TokenTree) -> String {
match tkn {
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident.text.clone().into(),
tt::TokenTree::Leaf(tt::Leaf::Literal(literal)) => literal.text.clone().into(),
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => format!("{}", punct.char),
tt::TokenTree::Subtree(subtree) => {
let content = tokentrees_to_text(&subtree.token_trees);
let (open, close) = match subtree.delimiter.map(|it| it.kind) {
None => ("", ""),
Some(tt::DelimiterKind::Brace) => ("{", "}"),
Some(tt::DelimiterKind::Parenthesis) => ("(", ")"),
Some(tt::DelimiterKind::Bracket) => ("[", "]"),
};
format!("{}{}{}", open, content, close)
}
}
}
} }
} }

View file

@ -199,44 +199,7 @@ pub mod token_stream {
impl ToString for TokenStream { impl ToString for TokenStream {
fn to_string(&self) -> String { fn to_string(&self) -> String {
return tokentrees_to_text(&self.token_trees[..]); tt::pretty(&self.token_trees)
fn tokentrees_to_text(tkns: &[tt::TokenTree]) -> String {
tkns.iter()
.fold((String::new(), true), |(last, last_to_joint), tkn| {
let s = [last, tokentree_to_text(tkn)].join(if last_to_joint {
""
} else {
" "
});
let mut is_joint = false;
if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn {
if punct.spacing == tt::Spacing::Joint {
is_joint = true;
}
}
(s, is_joint)
})
.0
}
fn tokentree_to_text(tkn: &tt::TokenTree) -> String {
match tkn {
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident.text.clone().into(),
tt::TokenTree::Leaf(tt::Leaf::Literal(literal)) => literal.text.clone().into(),
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => format!("{}", punct.char),
tt::TokenTree::Subtree(subtree) => {
let content = tokentrees_to_text(&subtree.token_trees);
let (open, close) = match subtree.delimiter.map(|it| it.kind) {
None => ("", ""),
Some(tt::DelimiterKind::Brace) => ("{", "}"),
Some(tt::DelimiterKind::Parenthesis) => ("(", ")"),
Some(tt::DelimiterKind::Bracket) => ("[", "]"),
};
format!("{}{}{}", open, content, close)
}
}
}
} }
} }

View file

@ -199,44 +199,7 @@ pub mod token_stream {
impl ToString for TokenStream { impl ToString for TokenStream {
fn to_string(&self) -> String { fn to_string(&self) -> String {
return tokentrees_to_text(&self.token_trees[..]); tt::pretty(&self.token_trees)
fn tokentrees_to_text(tkns: &[tt::TokenTree]) -> String {
tkns.iter()
.fold((String::new(), true), |(last, last_to_joint), tkn| {
let s = [last, tokentree_to_text(tkn)].join(if last_to_joint {
""
} else {
" "
});
let mut is_joint = false;
if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn {
if punct.spacing == tt::Spacing::Joint {
is_joint = true;
}
}
(s, is_joint)
})
.0
}
fn tokentree_to_text(tkn: &tt::TokenTree) -> String {
match tkn {
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident.text.clone().into(),
tt::TokenTree::Leaf(tt::Leaf::Literal(literal)) => literal.text.clone().into(),
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => format!("{}", punct.char),
tt::TokenTree::Subtree(subtree) => {
let content = tokentrees_to_text(&subtree.token_trees);
let (open, close) = match subtree.delimiter.map(|it| it.kind) {
None => ("", ""),
Some(tt::DelimiterKind::Brace) => ("{", "}"),
Some(tt::DelimiterKind::Parenthesis) => ("(", ")"),
Some(tt::DelimiterKind::Bracket) => ("[", "]"),
};
format!("{}{}{}", open, content, close)
}
}
}
} }
} }

View file

@ -274,3 +274,36 @@ impl Subtree {
} }
pub mod buffer; pub mod buffer;
pub fn pretty(tkns: &[TokenTree]) -> String {
fn tokentree_to_text(tkn: &TokenTree) -> String {
match tkn {
TokenTree::Leaf(Leaf::Ident(ident)) => ident.text.clone().into(),
TokenTree::Leaf(Leaf::Literal(literal)) => literal.text.clone().into(),
TokenTree::Leaf(Leaf::Punct(punct)) => format!("{}", punct.char),
TokenTree::Subtree(subtree) => {
let content = pretty(&subtree.token_trees);
let (open, close) = match subtree.delimiter.map(|it| it.kind) {
None => ("", ""),
Some(DelimiterKind::Brace) => ("{", "}"),
Some(DelimiterKind::Parenthesis) => ("(", ")"),
Some(DelimiterKind::Bracket) => ("[", "]"),
};
format!("{}{}{}", open, content, close)
}
}
}
tkns.iter()
.fold((String::new(), true), |(last, last_to_joint), tkn| {
let s = [last, tokentree_to_text(tkn)].join(if last_to_joint { "" } else { " " });
let mut is_joint = false;
if let TokenTree::Leaf(Leaf::Punct(punct)) = tkn {
if punct.spacing == Spacing::Joint {
is_joint = true;
}
}
(s, is_joint)
})
.0
}