// SPDX-License-Identifier: Apache-2.0 OR MIT
use crate::detection::inside_proc_macro;
use crate::fallback::{self, FromStr2 as _};
#[cfg(span_locations)]
use crate::location::LineColumn;
#[cfg(proc_macro_span)]
use crate::probe::proc_macro_span;
#[cfg(all(span_locations, proc_macro_span_file))]
use crate::probe::proc_macro_span_file;
#[cfg(all(span_locations, proc_macro_span_location))]
use crate::probe::proc_macro_span_location;
use crate::{Delimiter, Punct, Spacing, TokenTree};
use core::fmt::{self, Debug, Display};
#[cfg(span_locations)]
use core::ops::Range;
use core::ops::RangeBounds;
use std::ffi::CStr;
#[cfg(span_locations)]
use std::path::PathBuf;
#[derive(Clone)]
pub(crate) enum TokenStream {
Compiler(DeferredTokenStream),
Fallback(fallback::TokenStream),
}
// Work around https://github.com/rust-lang/rust/issues/65080.
// In `impl Extend<TokenTree> for TokenStream` which is used heavily by quote,
// we hold on to the appended tokens and do proc_macro::TokenStream::extend as
// late as possible to batch together consecutive uses of the Extend impl.
#[derive(Clone)]
pub(crate) struct DeferredTokenStream {
stream: proc_macro::TokenStream,
extra: Vec<proc_macro::TokenTree>,
}
pub(crate) enum LexError {
Compiler(proc_macro::LexError),
Fallback(fallback::LexError),
// Rustc was supposed to return a LexError, but it panicked instead.
// https://github.com/rust-lang/rust/issues/58736
CompilerPanic,
}
#[cold]
fn mismatch(line: u32) -> ! {
#[cfg(procmacro2_backtrace)]
{
let backtrace = std::backtrace::Backtrace::force_capture();
panic!("compiler/fallback mismatch L{}\n\n{}", line, backtrace)
}
#[cfg(not(procmacro2_backtrace))]
{
panic!("compiler/fallback mismatch L{}", line)
}
}
impl DeferredTokenStream {
fn new(stream: proc_macro::TokenStream) -> Self {
DeferredTokenStream {
stream,
extra: Vec::new(),
}
}
fn is_empty(&self) -> bool {
self.stream.is_empty() && self.extra.is_empty()
}
fn evaluate_now(&mut self) {
// If-check provides a fast short circuit for the common case of `extra`
// being empty, which saves a round trip over the proc macro bridge.
// Improves macro expansion time in winrt by 6% in debug mode.
if !self.extra.is_empty() {
self.stream.extend(self.extra.drain(..));
}
}
fn into_token_stream(mut self) -> proc_macro::TokenStream {
self.evaluate_now();
self.stream
}
}
impl TokenStream {
pub(crate) fn new() -> Self {
if inside_proc_macro() {
TokenStream::Compiler(DeferredTokenStream::new(proc_macro::TokenStream::new()))
} else {
TokenStream::Fallback(fallback::TokenStream::new())
}
}
pub(crate) fn from_str_checked(src: &str) -> Result<Self, LexError> {
if inside_proc_macro() {
Ok(TokenStream::Compiler(DeferredTokenStream::new(
proc_macro::TokenStream::from_str_checked(src)?,
)))
} else {
Ok(TokenStream::Fallback(
fallback::TokenStream::from_str_checked(src)?,
))
}
}
pub(crate) fn is_empty(&self) -> bool {
match self {
TokenStream::Compiler(tts) => tts.is_empty(),
TokenStream::Fallback(tts) => tts.is_empty(),
}
}
fn unwrap_nightly(self) -> proc_macro::TokenStream {
match self {
TokenStream::Compiler(s) => s.into_token_stream(),
TokenStream::Fallback(_) => mismatch(line!()),
}
}
fn unwrap_stable(self) -> fallback::TokenStream {
match self {
TokenStream::Compiler(_) => mismatch(line!()),
TokenStream::Fallback(s) => s,
}
}
}
impl Display for TokenStream {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TokenStream::Compiler(tts) => Display::fmt(&tts.clone().into_token_stream(), <