mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-03 10:33:34 +00:00
Merge #7802
7802: Fix builtin macros split exprs on comma r=edwin0cheng a=edwin0cheng Fixes #7640 bors r+ Co-authored-by: Edwin Cheng <edwin0cheng@gmail.com>
This commit is contained in:
commit
e0437f899c
@ -6,7 +6,7 @@ use crate::{
|
|||||||
|
|
||||||
use base_db::{AnchoredPath, FileId};
|
use base_db::{AnchoredPath, FileId};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use mbe::{parse_to_token_tree, ExpandResult};
|
use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult};
|
||||||
use parser::FragmentKind;
|
use parser::FragmentKind;
|
||||||
use syntax::ast::{self, AstToken};
|
use syntax::ast::{self, AstToken};
|
||||||
|
|
||||||
@ -238,35 +238,21 @@ fn format_args_expand(
|
|||||||
// ])
|
// ])
|
||||||
// ```,
|
// ```,
|
||||||
// which is still not really correct, but close enough for now
|
// which is still not really correct, but close enough for now
|
||||||
let mut args = Vec::new();
|
let mut args = parse_exprs_with_sep(tt, ',');
|
||||||
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(current);
|
|
||||||
current = Vec::new();
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
current.push(tt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !current.is_empty() {
|
|
||||||
args.push(current);
|
|
||||||
}
|
|
||||||
if args.is_empty() {
|
if args.is_empty() {
|
||||||
return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule);
|
return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule);
|
||||||
}
|
}
|
||||||
for arg in &mut args {
|
for arg in &mut args {
|
||||||
// Remove `key =`.
|
// Remove `key =`.
|
||||||
if matches!(arg.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' && p.spacing != tt::Spacing::Joint)
|
if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' && p.spacing != tt::Spacing::Joint)
|
||||||
{
|
{
|
||||||
arg.drain(..2);
|
arg.token_trees.drain(..2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let _format_string = args.remove(0);
|
let _format_string = args.remove(0);
|
||||||
let arg_tts = args.into_iter().flat_map(|arg| {
|
let arg_tts = args.into_iter().flat_map(|arg| {
|
||||||
quote! { std::fmt::ArgumentV1::new(&(##arg), std::fmt::Display::fmt), }
|
quote! { std::fmt::ArgumentV1::new(&(#arg), std::fmt::Display::fmt), }
|
||||||
}.token_trees).collect::<Vec<_>>();
|
}.token_trees).collect::<Vec<_>>();
|
||||||
let expanded = quote! {
|
let expanded = quote! {
|
||||||
std::fmt::Arguments::new_v1(&[], &[##arg_tts])
|
std::fmt::Arguments::new_v1(&[], &[##arg_tts])
|
||||||
@ -719,6 +705,25 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_format_args_expand_with_comma_exprs() {
|
||||||
|
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!("{} {:?}", a::<A,B>(), b);
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
expanded,
|
||||||
|
r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a::<A,B>()),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(b),std::fmt::Display::fmt),])"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_include_bytes_expand() {
|
fn test_include_bytes_expand() {
|
||||||
let expanded = expand_builtin_macro(
|
let expanded = expand_builtin_macro(
|
||||||
|
@ -3,15 +3,13 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
expander::{Binding, Bindings, Fragment},
|
expander::{Binding, Bindings, Fragment},
|
||||||
parser::{Op, RepeatKind, Separator},
|
parser::{Op, RepeatKind, Separator},
|
||||||
subtree_source::SubtreeTokenSource,
|
|
||||||
tt_iter::TtIter,
|
tt_iter::TtIter,
|
||||||
ExpandError, MetaTemplate,
|
ExpandError, MetaTemplate,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::ExpandResult;
|
use super::ExpandResult;
|
||||||
use parser::{FragmentKind::*, TreeSink};
|
use parser::FragmentKind::*;
|
||||||
use syntax::{SmolStr, SyntaxKind};
|
use syntax::SmolStr;
|
||||||
use tt::buffer::{Cursor, TokenBuffer};
|
|
||||||
|
|
||||||
impl Bindings {
|
impl Bindings {
|
||||||
fn push_optional(&mut self, name: &SmolStr) {
|
fn push_optional(&mut self, name: &SmolStr) {
|
||||||
@ -409,68 +407,6 @@ impl<'a> TtIter<'a> {
|
|||||||
.into())
|
.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expect_fragment(
|
|
||||||
&mut self,
|
|
||||||
fragment_kind: parser::FragmentKind,
|
|
||||||
) -> ExpandResult<Option<tt::TokenTree>> {
|
|
||||||
struct OffsetTokenSink<'a> {
|
|
||||||
cursor: Cursor<'a>,
|
|
||||||
error: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> TreeSink for OffsetTokenSink<'a> {
|
|
||||||
fn token(&mut self, kind: SyntaxKind, mut n_tokens: u8) {
|
|
||||||
if kind == SyntaxKind::LIFETIME_IDENT {
|
|
||||||
n_tokens = 2;
|
|
||||||
}
|
|
||||||
for _ in 0..n_tokens {
|
|
||||||
self.cursor = self.cursor.bump_subtree();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn start_node(&mut self, _kind: SyntaxKind) {}
|
|
||||||
fn finish_node(&mut self) {}
|
|
||||||
fn error(&mut self, _error: parser::ParseError) {
|
|
||||||
self.error = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let buffer = TokenBuffer::from_tokens(&self.inner.as_slice());
|
|
||||||
let mut src = SubtreeTokenSource::new(&buffer);
|
|
||||||
let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false };
|
|
||||||
|
|
||||||
parser::parse_fragment(&mut src, &mut sink, fragment_kind);
|
|
||||||
|
|
||||||
let mut err = None;
|
|
||||||
if !sink.cursor.is_root() || sink.error {
|
|
||||||
err = Some(err!("expected {:?}", fragment_kind));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut curr = buffer.begin();
|
|
||||||
let mut res = vec![];
|
|
||||||
|
|
||||||
if sink.cursor.is_root() {
|
|
||||||
while curr != sink.cursor {
|
|
||||||
if let Some(token) = curr.token_tree() {
|
|
||||||
res.push(token);
|
|
||||||
}
|
|
||||||
curr = curr.bump();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.inner = self.inner.as_slice()[res.len()..].iter();
|
|
||||||
if res.len() == 0 && err.is_none() {
|
|
||||||
err = Some(err!("no tokens consumed"));
|
|
||||||
}
|
|
||||||
let res = match res.len() {
|
|
||||||
1 => Some(res[0].cloned()),
|
|
||||||
0 => None,
|
|
||||||
_ => Some(tt::TokenTree::Subtree(tt::Subtree {
|
|
||||||
delimiter: None,
|
|
||||||
token_trees: res.into_iter().map(|it| it.cloned()).collect(),
|
|
||||||
})),
|
|
||||||
};
|
|
||||||
ExpandResult { value: res, err }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eat_vis(&mut self) -> Option<tt::TokenTree> {
|
fn eat_vis(&mut self) -> Option<tt::TokenTree> {
|
||||||
let mut fork = self.clone();
|
let mut fork = self.clone();
|
||||||
match fork.expect_fragment(Visibility) {
|
match fork.expect_fragment(Visibility) {
|
||||||
|
@ -65,8 +65,8 @@ impl fmt::Display for ExpandError {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub use crate::syntax_bridge::{
|
pub use crate::syntax_bridge::{
|
||||||
ast_to_token_tree, parse_to_token_tree, syntax_node_to_token_tree, token_tree_to_syntax_node,
|
ast_to_token_tree, parse_exprs_with_sep, parse_to_token_tree, syntax_node_to_token_tree,
|
||||||
TokenMap,
|
token_tree_to_syntax_node, TokenMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// This struct contains AST for a single `macro_rules` definition. What might
|
/// This struct contains AST for a single `macro_rules` definition. What might
|
||||||
|
@ -10,8 +10,8 @@ use syntax::{
|
|||||||
};
|
};
|
||||||
use tt::buffer::{Cursor, TokenBuffer};
|
use tt::buffer::{Cursor, TokenBuffer};
|
||||||
|
|
||||||
use crate::subtree_source::SubtreeTokenSource;
|
|
||||||
use crate::ExpandError;
|
use crate::ExpandError;
|
||||||
|
use crate::{subtree_source::SubtreeTokenSource, tt_iter::TtIter};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
|
||||||
pub enum TokenTextRange {
|
pub enum TokenTextRange {
|
||||||
@ -112,6 +112,43 @@ pub fn parse_to_token_tree(text: &str) -> Option<(tt::Subtree, TokenMap)> {
|
|||||||
Some((subtree, conv.id_alloc.map))
|
Some((subtree, conv.id_alloc.map))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Split token tree with seperate expr: $($e:expr)SEP*
|
||||||
|
pub fn parse_exprs_with_sep(tt: &tt::Subtree, sep: char) -> Vec<tt::Subtree> {
|
||||||
|
if tt.token_trees.is_empty() {
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut iter = TtIter::new(tt);
|
||||||
|
let mut res = Vec::new();
|
||||||
|
|
||||||
|
while iter.peek_n(0).is_some() {
|
||||||
|
let expanded = iter.expect_fragment(FragmentKind::Expr);
|
||||||
|
if expanded.err.is_some() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push(match expanded.value {
|
||||||
|
None => break,
|
||||||
|
Some(tt @ tt::TokenTree::Leaf(_)) => {
|
||||||
|
tt::Subtree { delimiter: None, token_trees: vec![tt.into()] }
|
||||||
|
}
|
||||||
|
Some(tt::TokenTree::Subtree(tt)) => tt,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut fork = iter.clone();
|
||||||
|
if fork.expect_char(sep).is_err() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
iter = fork;
|
||||||
|
}
|
||||||
|
|
||||||
|
if iter.peek_n(0).is_some() {
|
||||||
|
res.push(tt::Subtree { delimiter: None, token_trees: iter.into_iter().cloned().collect() });
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
impl TokenMap {
|
impl TokenMap {
|
||||||
pub fn token_by_range(&self, relative_range: TextRange) -> Option<tt::TokenId> {
|
pub fn token_by_range(&self, relative_range: TextRange) -> Option<tt::TokenId> {
|
||||||
let &(token_id, _) = self.entries.iter().find(|(_, range)| match range {
|
let &(token_id, _) = self.entries.iter().find(|(_, range)| match range {
|
||||||
|
@ -1,5 +1,20 @@
|
|||||||
//! FIXME: write short doc here
|
//! FIXME: write short doc here
|
||||||
|
|
||||||
|
use crate::{subtree_source::SubtreeTokenSource, ExpandError, ExpandResult};
|
||||||
|
|
||||||
|
use parser::TreeSink;
|
||||||
|
use syntax::SyntaxKind;
|
||||||
|
use tt::buffer::{Cursor, TokenBuffer};
|
||||||
|
|
||||||
|
macro_rules! err {
|
||||||
|
() => {
|
||||||
|
ExpandError::BindingError(format!(""))
|
||||||
|
};
|
||||||
|
($($tt:tt)*) => {
|
||||||
|
ExpandError::BindingError(format!($($tt)*))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(crate) struct TtIter<'a> {
|
pub(crate) struct TtIter<'a> {
|
||||||
pub(crate) inner: std::slice::Iter<'a, tt::TokenTree>,
|
pub(crate) inner: std::slice::Iter<'a, tt::TokenTree>,
|
||||||
@ -56,6 +71,68 @@ impl<'a> TtIter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn expect_fragment(
|
||||||
|
&mut self,
|
||||||
|
fragment_kind: parser::FragmentKind,
|
||||||
|
) -> ExpandResult<Option<tt::TokenTree>> {
|
||||||
|
struct OffsetTokenSink<'a> {
|
||||||
|
cursor: Cursor<'a>,
|
||||||
|
error: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TreeSink for OffsetTokenSink<'a> {
|
||||||
|
fn token(&mut self, kind: SyntaxKind, mut n_tokens: u8) {
|
||||||
|
if kind == SyntaxKind::LIFETIME_IDENT {
|
||||||
|
n_tokens = 2;
|
||||||
|
}
|
||||||
|
for _ in 0..n_tokens {
|
||||||
|
self.cursor = self.cursor.bump_subtree();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn start_node(&mut self, _kind: SyntaxKind) {}
|
||||||
|
fn finish_node(&mut self) {}
|
||||||
|
fn error(&mut self, _error: parser::ParseError) {
|
||||||
|
self.error = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let buffer = TokenBuffer::from_tokens(&self.inner.as_slice());
|
||||||
|
let mut src = SubtreeTokenSource::new(&buffer);
|
||||||
|
let mut sink = OffsetTokenSink { cursor: buffer.begin(), error: false };
|
||||||
|
|
||||||
|
parser::parse_fragment(&mut src, &mut sink, fragment_kind);
|
||||||
|
|
||||||
|
let mut err = None;
|
||||||
|
if !sink.cursor.is_root() || sink.error {
|
||||||
|
err = Some(err!("expected {:?}", fragment_kind));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut curr = buffer.begin();
|
||||||
|
let mut res = vec![];
|
||||||
|
|
||||||
|
if sink.cursor.is_root() {
|
||||||
|
while curr != sink.cursor {
|
||||||
|
if let Some(token) = curr.token_tree() {
|
||||||
|
res.push(token);
|
||||||
|
}
|
||||||
|
curr = curr.bump();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.inner = self.inner.as_slice()[res.len()..].iter();
|
||||||
|
if res.len() == 0 && err.is_none() {
|
||||||
|
err = Some(err!("no tokens consumed"));
|
||||||
|
}
|
||||||
|
let res = match res.len() {
|
||||||
|
1 => Some(res[0].cloned()),
|
||||||
|
0 => None,
|
||||||
|
_ => Some(tt::TokenTree::Subtree(tt::Subtree {
|
||||||
|
delimiter: None,
|
||||||
|
token_trees: res.into_iter().map(|it| it.cloned()).collect(),
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
ExpandResult { value: res, err }
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn peek_n(&self, n: usize) -> Option<&tt::TokenTree> {
|
pub(crate) fn peek_n(&self, n: usize) -> Option<&tt::TokenTree> {
|
||||||
self.inner.as_slice().get(n)
|
self.inner.as_slice().get(n)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user