use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCursor, TrailingToken};
use rustc_ast::token::{self, DelimToken, Token, TokenKind};
use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttributesData, CreateTokenStream};
use rustc_ast::tokenstream::{AttrAnnotatedTokenTree, DelimSpan, LazyTokenStream, Spacing};
use rustc_ast::{self as ast};
use rustc_ast::{AstLike, AttrVec, Attribute};
use rustc_errors::PResult;
use rustc_span::{sym, Span, DUMMY_SP};
use std::convert::TryInto;
use std::ops::Range;
#[derive(Debug, Clone)]
pub struct AttrWrapper {
attrs: AttrVec,
start_pos: usize,
}
#[cfg(target_arch = "x86_64")]
rustc_data_structures::static_assert_size!(AttrWrapper, 16);
impl AttrWrapper {
pub(super) fn new(attrs: AttrVec, start_pos: usize) -> AttrWrapper {
AttrWrapper { attrs, start_pos }
}
pub fn empty() -> AttrWrapper {
AttrWrapper { attrs: AttrVec::new(), start_pos: usize::MAX }
}
pub(crate) fn take_for_recovery(self) -> AttrVec {
self.attrs
}
pub(crate) fn prepend_to_nt_inner(self, attrs: &mut Vec<Attribute>) {
let mut self_attrs: Vec<_> = self.attrs.into();
std::mem::swap(attrs, &mut self_attrs);
attrs.extend(self_attrs);
}
pub fn is_empty(&self) -> bool {
self.attrs.is_empty()
}
pub fn maybe_needs_tokens(&self) -> bool {
crate::parser::attr::maybe_needs_tokens(&self.attrs)
}
}
fn has_cfg_or_cfg_attr(attrs: &[Attribute]) -> bool {
attrs.iter().any(|attr| {
attr.ident().map_or(false, |ident| ident.name == sym::cfg || ident.name == sym::cfg_attr)
})
}
#[derive(Clone)]
struct LazyTokenStreamImpl {
start_token: (Token, Spacing),
cursor_snapshot: TokenCursor,
num_calls: usize,
break_last_token: bool,
replace_ranges: Box<[ReplaceRange]>,
}
#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
rustc_data_structures::static_assert_size!(LazyTokenStreamImpl, 144);
impl CreateTokenStream for LazyTokenStreamImpl {
fn create_token_stream(&self) -> AttrAnnotatedTokenStream {
let mut cursor_snapshot = self.cursor_snapshot.clone();
let tokens =
std::iter::once((FlatToken::Token(self.start_token.0.clone()), self.start_token.1))
.chain((0..self.num_calls).map(|_| {
let token = if cursor_snapshot.desugar_doc_comments {
cursor_snapshot.next_desugared()
} else {
cursor_snapshot.next()
};
(FlatToken::Token(token.0), token.1)
}))
.take(self.num_calls);
if !self.replace_ranges.is_empty() {
let mut tokens: Vec<_> = tokens.collect();
let mut replace_ranges = self.replace_ranges.clone();
replace_ranges.sort_by_key(|(range, _)| range.start);
#[cfg(debug_assertions)]
{
for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() {
assert!(
range.end <= next_range.start || range.end >= next_range.end,
"Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})",
range,
tokens,
next_range,
next_tokens,
);
}
}
for (range, new_tokens) in replace_ranges.iter().rev() {
assert!(!range.is_empty(), "Cannot replace an empty range: {:?}", range);
assert!(
range.len() >= new_tokens.len(),
"Range {:?} has greater len than {:?}",
range,
new_tokens
);
let filler = std::iter::repeat((FlatToken::Empty, Spacing::Alone))
.take(range.len() - new_tokens.len());
tokens.splice(
(range.start as usize)..(range.end as usize),
new_tokens.clone().into_iter().chain(filler),
);
}
make_token_stream(tokens.into_iter(), self.break_last_token)
} else {
make_token_stream(tokens, self.break_last_token)
}
}
}
impl<'a> Parser<'a> {
pub fn collect_tokens_trailing_token<R: AstLike>(
&mut self,
attrs: AttrWrapper,
force_collect: ForceCollect,
f: impl FnOnce(&mut Self, Vec<ast::Attribute>) -> PResult<'a, (R, TrailingToken)>,
) -> PResult<'a, R> {
if matches!(force_collect, ForceCollect::No)
&& !attrs.maybe_needs_tokens()
&& !R::SUPPORTS_CUSTOM_INNER_ATTRS
&& !self.capture_cfg
{
return Ok(f(self, attrs.attrs.into())?.0);
}
let start_token = (self.token.clone(), self.token_spacing);
let cursor_snapshot = self.token_cursor.clone();
let has_outer_attrs = !attrs.attrs.is_empty();
let prev_capturing = std::mem::replace(&mut self.capture_state.capturing, Capturing::Yes);
let replace_ranges_start = self.capture_state.replace_ranges.len();
let ret = f(self, attrs.attrs.into());
self.capture_state.capturing = prev_capturing;
let (mut ret, trailing) = ret?;
if !self.capture_cfg && matches!(ret.tokens_mut(), None | Some(Some(_))) {
return Ok(ret);
}
if matches!(force_collect, ForceCollect::No)
&& !crate::parser::attr::maybe_needs_tokens(ret.attrs())
&& !(self.capture_cfg && has_cfg_or_cfg_attr(ret.attrs()))
{
return Ok(ret);
}
let mut inner_attr_replace_ranges = Vec::new();
for inner_attr in ret.attrs().iter().filter(|a| a.style == ast::AttrStyle::Inner) {
if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&inner_attr.id) {
inner_attr_replace_ranges.push(attr_range);
} else {
self.sess
.span_diagnostic
.delay_span_bug(inner_attr.span, "Missing token range for attribute");
}
}
let replace_ranges_end = self.capture_state.replace_ranges.len();
let cursor_snapshot_next_calls = cursor_snapshot.num_next_calls;
let mut end_pos = self.token_cursor.num_next_calls;
match trailing {
TrailingToken::None => {}
TrailingToken::Semi => {
assert_eq!(self.token.kind, token::Semi);
end_pos += 1;
}
TrailingToken::MaybeComma => {
if self.token.kind == token::Comma {
end_pos += 1;
}
}
}
if self.token_cursor.break_last_token {
assert_eq!(
trailing,
TrailingToken::None,
"Cannot set `break_last_token` and have trailing token"
);
end_pos += 1;
}
let num_calls = end_pos - cursor_snapshot_next_calls;
let replace_ranges: Box<[ReplaceRange]> = if ret.attrs().is_empty() && !self.capture_cfg {
Box::new([])
} else {
let start_calls: u32 = cursor_snapshot_next_calls.try_into().unwrap();
self.capture_state.replace_ranges[replace_ranges_start..replace_ranges_end]
.iter()
.cloned()
.chain(inner_attr_replace_ranges.clone().into_iter())
.map(|(range, tokens)| {
((range.start - start_calls)..(range.end - start_calls), tokens)
})
.collect()
};
let tokens = LazyTokenStream::new(LazyTokenStreamImpl {
start_token,
num_calls,
cursor_snapshot,
break_last_token: self.token_cursor.break_last_token,
replace_ranges,
});
if let Some(target_tokens) = ret.tokens_mut() {
if target_tokens.is_none() {
*target_tokens = Some(tokens.clone());
}
}
let final_attrs = ret.attrs();
if self.capture_cfg
&& matches!(self.capture_state.capturing, Capturing::Yes)
&& has_cfg_or_cfg_attr(&final_attrs)
{
let attr_data = AttributesData { attrs: final_attrs.to_vec().into(), tokens };
let start_pos =
if has_outer_attrs { attrs.start_pos } else { cursor_snapshot_next_calls };
let new_tokens = vec![(FlatToken::AttrTarget(attr_data), Spacing::Alone)];
assert!(
!self.token_cursor.break_last_token,
"Should not have unglued last token with cfg attr"
);
let range: Range<u32> = (start_pos.try_into().unwrap())..(end_pos.try_into().unwrap());
self.capture_state.replace_ranges.push((range, new_tokens));
self.capture_state.replace_ranges.extend(inner_attr_replace_ranges);
}
if matches!(self.capture_state.capturing, Capturing::No) {
self.capture_state.replace_ranges.clear();
}
Ok(ret)
}
}
fn make_token_stream(
mut iter: impl Iterator<Item = (FlatToken, Spacing)>,
break_last_token: bool,
) -> AttrAnnotatedTokenStream {
#[derive(Debug)]
struct FrameData {
open: Span,
open_delim: DelimToken,
inner: Vec<(AttrAnnotatedTokenTree, Spacing)>,
}
let mut stack =
vec![FrameData { open: DUMMY_SP, open_delim: DelimToken::NoDelim, inner: vec![] }];
let mut token_and_spacing = iter.next();
while let Some((token, spacing)) = token_and_spacing {
match token {
FlatToken::Token(Token { kind: TokenKind::OpenDelim(delim), span }) => {
stack.push(FrameData { open: span, open_delim: delim, inner: vec![] });
}
FlatToken::Token(Token { kind: TokenKind::CloseDelim(delim), span }) => {
if matches!(delim, DelimToken::NoDelim)
&& (stack.len() == 1
|| !matches!(stack.last_mut().unwrap().open_delim, DelimToken::NoDelim))
{
token_and_spacing = iter.next();
continue;
}
let frame_data = stack
.pop()
.unwrap_or_else(|| panic!("Token stack was empty for token: {:?}", token));
if !matches!(delim, DelimToken::NoDelim)
&& matches!(frame_data.open_delim, DelimToken::NoDelim)
{
stack.last_mut().unwrap().inner.extend(frame_data.inner);
token_and_spacing = Some((token, spacing));
continue;
}
assert_eq!(
frame_data.open_delim, delim,
"Mismatched open/close delims: open={:?} close={:?}",
frame_data.open, span
);
let dspan = DelimSpan::from_pair(frame_data.open, span);
let stream = AttrAnnotatedTokenStream::new(frame_data.inner);
let delimited = AttrAnnotatedTokenTree::Delimited(dspan, delim, stream);
stack
.last_mut()
.unwrap_or_else(|| {
panic!("Bottom token frame is missing for token: {:?}", token)
})
.inner
.push((delimited, Spacing::Alone));
}
FlatToken::Token(token) => stack
.last_mut()
.expect("Bottom token frame is missing!")
.inner
.push((AttrAnnotatedTokenTree::Token(token), spacing)),
FlatToken::AttrTarget(data) => stack
.last_mut()
.expect("Bottom token frame is missing!")
.inner
.push((AttrAnnotatedTokenTree::Attributes(data), spacing)),
FlatToken::Empty => {}
}
token_and_spacing = iter.next();
}
if stack.len() == 2 && stack[1].open_delim == DelimToken::NoDelim {
let temp_buf = stack.pop().unwrap();
stack.last_mut().unwrap().inner.extend(temp_buf.inner);
}
let mut final_buf = stack.pop().expect("Missing final buf!");
if break_last_token {
let (last_token, spacing) = final_buf.inner.pop().unwrap();
if let AttrAnnotatedTokenTree::Token(last_token) = last_token {
let unglued_first = last_token.kind.break_two_token_op().unwrap().0;
let mut first_span = last_token.span.shrink_to_lo();
first_span = first_span.with_hi(first_span.lo() + rustc_span::BytePos(1));
final_buf.inner.push((
AttrAnnotatedTokenTree::Token(Token::new(unglued_first, first_span)),
spacing,
));
} else {
panic!("Unexpected last token {:?}", last_token)
}
}
assert!(stack.is_empty(), "Stack should be empty: final_buf={:?} stack={:?}", final_buf, stack);
AttrAnnotatedTokenStream::new(final_buf.inner)
}