#![deny(missing_docs)]
#![allow(internal_features)]
#![deny(ffi_unwind_calls)]#![warn(rustdoc::unescaped_backticks)]
#![warn(unreachable_pub)]
#![deny(unsafe_op_in_unsafe_fn)]
use alloc::borrow::ToOwned;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
#[doc(hidden)]
pub mod bridge;
mod diagnostic;
mod escape;
mod to_tokens;
use core::convert::From;
use core::marker::PhantomData;
use core::ops::BitOr;
use alloc::borrow::Cow;
use core::ffi::CStr;
use core::ops::{Range, RangeBounds};
use core::str::FromStr;
use core::{error, fmt};
use eko::path::PathBuf;
pub use diagnostic::{Diagnostic, Level, MultiSpan};
use rustc_literal_escaper::{
MixedUnit, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char, unescape_str,
};
pub use to_tokens::ToTokens;
use crate::rustc_proc_macro::bridge::client::Methods as BridgeMethods;
use crate::rustc_proc_macro::escape::{EscapeOptions, escape_bytes};
#[derive(Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum EscapeError {
ZeroChars,
MoreThanOneChar,
LoneSlash,
InvalidEscape,
BareCarriageReturn,
BareCarriageReturnInRawString,
EscapeOnlyChar,
TooShortHexEscape,
InvalidCharInHexEscape,
OutOfRangeHexEscape,
NoBraceInUnicodeEscape,
InvalidCharInUnicodeEscape,
EmptyUnicodeEscape,
UnclosedUnicodeEscape,
LeadingUnderscoreUnicodeEscape,
OverlongUnicodeEscape,
LoneSurrogateUnicodeEscape,
OutOfRangeUnicodeEscape,
UnicodeEscapeInByte,
NonAsciiCharInByte,
NulInCStr,
UnskippedWhitespaceWarning,
MultipleSkippedLinesWarning,
}
#[doc(hidden)]
impl From<rustc_literal_escaper::EscapeError> for EscapeError {
fn from(value: rustc_literal_escaper::EscapeError) -> Self {
use rustc_literal_escaper::EscapeError as EE;
match value {
EE::ZeroChars => Self::ZeroChars,
EE::MoreThanOneChar => Self::MoreThanOneChar,
EE::LoneSlash => Self::LoneSlash,
EE::InvalidEscape => Self::InvalidEscape,
EE::BareCarriageReturn => Self::BareCarriageReturn,
EE::BareCarriageReturnInRawString => Self::BareCarriageReturnInRawString,
EE::EscapeOnlyChar => Self::EscapeOnlyChar,
EE::TooShortHexEscape => Self::TooShortHexEscape,
EE::InvalidCharInHexEscape => Self::InvalidCharInHexEscape,
EE::OutOfRangeHexEscape => Self::OutOfRangeHexEscape,
EE::NoBraceInUnicodeEscape => Self::NoBraceInUnicodeEscape,
EE::InvalidCharInUnicodeEscape => Self::InvalidCharInUnicodeEscape,
EE::EmptyUnicodeEscape => Self::EmptyUnicodeEscape,
EE::UnclosedUnicodeEscape => Self::UnclosedUnicodeEscape,
EE::LeadingUnderscoreUnicodeEscape => Self::LeadingUnderscoreUnicodeEscape,
EE::OverlongUnicodeEscape => Self::OverlongUnicodeEscape,
EE::LoneSurrogateUnicodeEscape => Self::LoneSurrogateUnicodeEscape,
EE::OutOfRangeUnicodeEscape => Self::OutOfRangeUnicodeEscape,
EE::UnicodeEscapeInByte => Self::UnicodeEscapeInByte,
EE::NonAsciiCharInByte => Self::NonAsciiCharInByte,
EE::NulInCStr => Self::NulInCStr,
EE::UnskippedWhitespaceWarning => Self::UnskippedWhitespaceWarning,
EE::MultipleSkippedLinesWarning => Self::MultipleSkippedLinesWarning,
}
}
}
impl error::Error for EscapeError {}
impl fmt::Display for EscapeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::ZeroChars => "zero chars",
Self::MoreThanOneChar => "more than one char",
Self::LoneSlash => "lone slash",
Self::InvalidEscape => "invalid escape",
Self::BareCarriageReturn => "bare carriage return",
Self::BareCarriageReturnInRawString => "bare carriage return in raw string",
Self::EscapeOnlyChar => "escape only char",
Self::TooShortHexEscape => "too short hex escape",
Self::InvalidCharInHexEscape => "invalid char in hex escape",
Self::OutOfRangeHexEscape => "out of range hex escape",
Self::NoBraceInUnicodeEscape => "no brace in unicode escape",
Self::InvalidCharInUnicodeEscape => "invalid char in unicode escape",
Self::EmptyUnicodeEscape => "empty unicode escape",
Self::UnclosedUnicodeEscape => "unclosed unicode escape",
Self::LeadingUnderscoreUnicodeEscape => "leading underscore unicode escape",
Self::OverlongUnicodeEscape => "overlong unicode escape",
Self::LoneSurrogateUnicodeEscape => "lone surrogate unicode escape",
Self::OutOfRangeUnicodeEscape => "out of range unicode escape",
Self::UnicodeEscapeInByte => "unicode escape in byte",
Self::NonAsciiCharInByte => "non ascii char in byte",
Self::NulInCStr => "nul in CStr",
Self::UnskippedWhitespaceWarning => "unskipped whitespace warning",
Self::MultipleSkippedLinesWarning => "multiple skipped lines warning",
})
}
}
#[derive(Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ConversionErrorKind {
FailedToUnescape(EscapeError),
InvalidLiteralKind,
}
pub fn is_available() -> bool {
bridge::client::is_available()
}
#[derive(Clone)]
pub struct TokenStream(Option<bridge::client::TokenStream>);
pub struct LexError(String, PhantomData<*const ()>);
impl fmt::Debug for LexError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("LexError").field(&self.0).finish()
}
}
impl fmt::Display for LexError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl error::Error for LexError {}
#[non_exhaustive]
pub struct ExpandError(PhantomData<*const ()>);
impl fmt::Debug for ExpandError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("ExpandError")
}
}
impl fmt::Display for ExpandError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("macro expansion failed")
}
}
impl error::Error for ExpandError {}
impl TokenStream {
pub fn new() -> TokenStream {
TokenStream(None)
}
pub fn is_empty(&self) -> bool {
self.0.as_ref().map(BridgeMethods::ts_is_empty).unwrap_or(true)
}
pub fn expand_expr(&self) -> Result<TokenStream, ExpandError> {
let stream = self.0.as_ref().ok_or(ExpandError(PhantomData))?;
match BridgeMethods::ts_expand_expr(stream) {
Ok(stream) => Ok(TokenStream(Some(stream))),
Err(_) => Err(ExpandError(PhantomData)),
}
}
}
impl FromStr for TokenStream {
type Err = LexError;
fn from_str(src: &str) -> Result<TokenStream, LexError> {
Ok(TokenStream(Some(BridgeMethods::ts_from_str(src).map_err(|msg| LexError(msg, PhantomData))?)))
}
}
impl fmt::Display for TokenStream {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.0 {
Some(ts) => write!(f, "{}", BridgeMethods::ts_to_string(ts)),
None => Ok(()),
}
}
}
impl fmt::Debug for TokenStream {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("TokenStream ")?;
f.debug_list().entries(self.clone()).finish()
}
}
impl Default for TokenStream {
fn default() -> Self {
TokenStream::new()
}
}
pub use quote::{HasIterator, RepInterp, ThereIsNoIteratorInRepetition, ext, quote, quote_span};
fn tree_to_bridge_tree(
tree: TokenTree,
) -> bridge::TokenTree<bridge::client::TokenStream, bridge::client::Span, bridge::client::Symbol> {
match tree {
TokenTree::Group(tt) => bridge::TokenTree::Group(tt.0),
TokenTree::Punct(tt) => bridge::TokenTree::Punct(tt.0),
TokenTree::Ident(tt) => bridge::TokenTree::Ident(tt.0),
TokenTree::Literal(tt) => bridge::TokenTree::Literal(tt.0),
}
}
impl From<TokenTree> for TokenStream {
fn from(tree: TokenTree) -> TokenStream {
TokenStream(Some(BridgeMethods::ts_from_token_tree(tree_to_bridge_tree(tree))))
}
}
struct ConcatTreesHelper {
trees: Vec<
bridge::TokenTree<
bridge::client::TokenStream,
bridge::client::Span,
bridge::client::Symbol,
>,
>,
}
impl ConcatTreesHelper {
fn new(capacity: usize) -> Self {
ConcatTreesHelper { trees: Vec::with_capacity(capacity) }
}
fn push(&mut self, tree: TokenTree) {
self.trees.push(tree_to_bridge_tree(tree));
}
fn build(self) -> TokenStream {
if self.trees.is_empty() {
TokenStream(None)
} else {
TokenStream(Some(BridgeMethods::ts_concat_trees(None, self.trees)))
}
}
fn append_to(self, stream: &mut TokenStream) {
if self.trees.is_empty() {
return;
}
stream.0 = Some(BridgeMethods::ts_concat_trees(stream.0.take(), self.trees))
}
}
struct ConcatStreamsHelper {
streams: Vec<bridge::client::TokenStream>,
}
impl ConcatStreamsHelper {
fn new(capacity: usize) -> Self {
ConcatStreamsHelper { streams: Vec::with_capacity(capacity) }
}
fn push(&mut self, stream: TokenStream) {
if let Some(stream) = stream.0 {
self.streams.push(stream);
}
}
fn build(mut self) -> TokenStream {
if self.streams.len() <= 1 {
TokenStream(self.streams.pop())
} else {
TokenStream(Some(BridgeMethods::ts_concat_streams(None, self.streams)))
}
}
fn append_to(mut self, stream: &mut TokenStream) {
if self.streams.is_empty() {
return;
}
let base = stream.0.take();
if base.is_none() && self.streams.len() == 1 {
stream.0 = self.streams.pop();
} else {
stream.0 = Some(BridgeMethods::ts_concat_streams(base, self.streams));
}
}
}
impl FromIterator<TokenTree> for TokenStream {
fn from_iter<I: IntoIterator<Item = TokenTree>>(trees: I) -> Self {
let iter = trees.into_iter();
let mut builder = ConcatTreesHelper::new(iter.size_hint().0);
iter.for_each(|tree| builder.push(tree));
builder.build()
}
}
impl FromIterator<TokenStream> for TokenStream {
fn from_iter<I: IntoIterator<Item = TokenStream>>(streams: I) -> Self {
let iter = streams.into_iter();
let mut builder = ConcatStreamsHelper::new(iter.size_hint().0);
iter.for_each(|stream| builder.push(stream));
builder.build()
}
}
impl Extend<TokenTree> for TokenStream {
fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, trees: I) {
let iter = trees.into_iter();
let mut builder = ConcatTreesHelper::new(iter.size_hint().0);
iter.for_each(|tree| builder.push(tree));
builder.append_to(self);
}
}
impl Extend<TokenStream> for TokenStream {
fn extend<I: IntoIterator<Item = TokenStream>>(&mut self, streams: I) {
let iter = streams.into_iter();
let mut builder = ConcatStreamsHelper::new(iter.size_hint().0);
iter.for_each(|stream| builder.push(stream));
builder.append_to(self);
}
}
macro_rules! extend_items {
($($item:ident)*) => {
$(
impl Extend<$item> for TokenStream {
fn extend<T: IntoIterator<Item = $item>>(&mut self, iter: T) {
self.extend(iter.into_iter().map(TokenTree::$item));
}
}
)*
};
}
extend_items!(Group Literal Punct Ident);
pub mod token_stream {
use crate::rustc_proc_macro::{BridgeMethods, Group, Ident, Literal, Punct, TokenStream, TokenTree, bridge};
#[derive(Clone)]
pub struct IntoIter(
alloc::vec::IntoIter<
bridge::TokenTree<
bridge::client::TokenStream,
bridge::client::Span,
bridge::client::Symbol,
>,
>,
);
impl Iterator for IntoIter {
type Item = TokenTree;
fn next(&mut self) -> Option<TokenTree> {
self.0.next().map(|tree| match tree {
bridge::TokenTree::Group(tt) => TokenTree::Group(Group(tt)),
bridge::TokenTree::Punct(tt) => TokenTree::Punct(Punct(tt)),
bridge::TokenTree::Ident(tt) => TokenTree::Ident(Ident(tt)),
bridge::TokenTree::Literal(tt) => TokenTree::Literal(Literal(tt)),
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
fn count(self) -> usize {
self.0.count()
}
}
impl IntoIterator for TokenStream {
type Item = TokenTree;
type IntoIter = IntoIter;
fn into_iter(self) -> IntoIter {
IntoIter(self.0.map(BridgeMethods::ts_into_trees).unwrap_or_default().into_iter())
}
}
}
#[doc(hidden)]
mod quote;
#[derive(Copy, Clone)]
pub struct Span(bridge::client::Span);
macro_rules! diagnostic_method {
($name:ident, $level:expr) => {
pub fn $name<T: Into<String>>(self, message: T) -> Diagnostic {
Diagnostic::spanned(self, $level, message)
}
};
}
impl Span {
pub fn def_site() -> Span {
Span(bridge::client::Span::def_site())
}
pub fn call_site() -> Span {
Span(bridge::client::Span::call_site())
}
pub fn mixed_site() -> Span {
Span(bridge::client::Span::mixed_site())
}
pub fn parent(&self) -> Option<Span> {
BridgeMethods::span_parent(self.0).map(Span)
}
pub fn source(&self) -> Span {
Span(BridgeMethods::span_source(self.0))
}
pub fn byte_range(&self) -> Range<usize> {
BridgeMethods::span_byte_range(self.0)
}
pub fn start(&self) -> Span {
Span(BridgeMethods::span_start(self.0))
}
pub fn end(&self) -> Span {
Span(BridgeMethods::span_end(self.0))
}
pub fn line(&self) -> usize {
BridgeMethods::span_line(self.0)
}
pub fn column(&self) -> usize {
BridgeMethods::span_column(self.0)
}
pub fn file(&self) -> String {
BridgeMethods::span_file(self.0)
}
pub fn local_file(&self) -> Option<PathBuf> {
BridgeMethods::span_local_file(self.0).map(PathBuf::from)
}
pub fn join(&self, other: Span) -> Option<Span> {
BridgeMethods::span_join(self.0, other.0).map(Span)
}
pub fn resolved_at(&self, other: Span) -> Span {
Span(BridgeMethods::span_resolved_at(self.0, other.0))
}
pub fn located_at(&self, other: Span) -> Span {
other.resolved_at(*self)
}
pub fn eq(&self, other: &Span) -> bool {
self.0 == other.0
}
pub fn source_text(&self) -> Option<String> {
BridgeMethods::span_source_text(self.0)
}
#[doc(hidden)]
pub fn save_span(&self) -> usize {
BridgeMethods::span_save_span(self.0)
}
#[doc(hidden)]
pub fn recover_proc_macro_span(id: usize) -> Span {
Span(BridgeMethods::span_recover_proc_macro_span(id))
}
diagnostic_method!(error, Level::Error);
diagnostic_method!(warning, Level::Warning);
diagnostic_method!(note, Level::Note);
diagnostic_method!(help, Level::Help);
}
impl fmt::Debug for Span {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
#[derive(Clone)]
pub enum TokenTree {
Group(Group),
Ident(Ident),
Punct(Punct),
Literal(Literal),
}
impl TokenTree {
pub fn span(&self) -> Span {
match *self {
TokenTree::Group(ref t) => t.span(),
TokenTree::Ident(ref t) => t.span(),
TokenTree::Punct(ref t) => t.span(),
TokenTree::Literal(ref t) => t.span(),
}
}
pub fn set_span(&mut self, span: Span) {
match *self {
TokenTree::Group(ref mut t) => t.set_span(span),
TokenTree::Ident(ref mut t) => t.set_span(span),
TokenTree::Punct(ref mut t) => t.set_span(span),
TokenTree::Literal(ref mut t) => t.set_span(span),
}
}
}
impl fmt::Debug for TokenTree {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
TokenTree::Group(ref tt) => tt.fmt(f),
TokenTree::Ident(ref tt) => tt.fmt(f),
TokenTree::Punct(ref tt) => tt.fmt(f),
TokenTree::Literal(ref tt) => tt.fmt(f),
}
}
}
impl From<Group> for TokenTree {
fn from(g: Group) -> TokenTree {
TokenTree::Group(g)
}
}
impl From<Ident> for TokenTree {
fn from(g: Ident) -> TokenTree {
TokenTree::Ident(g)
}
}
impl From<Punct> for TokenTree {
fn from(g: Punct) -> TokenTree {
TokenTree::Punct(g)
}
}
impl From<Literal> for TokenTree {
fn from(g: Literal) -> TokenTree {
TokenTree::Literal(g)
}
}
impl fmt::Display for TokenTree {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TokenTree::Group(t) => write!(f, "{t}"),
TokenTree::Ident(t) => write!(f, "{t}"),
TokenTree::Punct(t) => write!(f, "{t}"),
TokenTree::Literal(t) => write!(f, "{t}"),
}
}
}
#[derive(Clone)]
pub struct Group(bridge::Group<bridge::client::TokenStream, bridge::client::Span>);
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Delimiter {
Parenthesis,
Brace,
Bracket,
None,
}
impl Group {
pub fn new(delimiter: Delimiter, stream: TokenStream) -> Group {
Group(bridge::Group {
delimiter,
stream: stream.0,
span: bridge::DelimSpan::from_single(Span::call_site().0),
})
}
pub fn delimiter(&self) -> Delimiter {
self.0.delimiter
}
pub fn stream(&self) -> TokenStream {
TokenStream(self.0.stream.clone())
}
pub fn span(&self) -> Span {
Span(self.0.span.entire)
}
pub fn span_open(&self) -> Span {
Span(self.0.span.open)
}
pub fn span_close(&self) -> Span {
Span(self.0.span.close)
}
pub fn set_span(&mut self, span: Span) {
self.0.span = bridge::DelimSpan::from_single(span.0);
}
}
impl fmt::Display for Group {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", TokenStream::from(TokenTree::from(self.clone())))
}
}
impl fmt::Debug for Group {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Group")
.field("delimiter", &self.delimiter())
.field("stream", &self.stream())
.field("span", &self.span())
.finish()
}
}
#[derive(Clone)]
pub struct Punct(bridge::Punct<bridge::client::Span>);
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Spacing {
Joint,
Alone,
}
impl Punct {
pub fn new(ch: char, spacing: Spacing) -> Punct {
const LEGAL_CHARS: &[char] = &[
'=', '<', '>', '!', '~', '+', '-', '*', '/', '%', '^', '&', '|', '@', '.', ',', ';',
':', '#', '$', '?', '\'',
];
if !LEGAL_CHARS.contains(&ch) {
panic!("unsupported character `{:?}`", ch);
}
Punct(bridge::Punct {
ch: ch as u8,
joint: spacing == Spacing::Joint,
span: Span::call_site().0,
})
}
pub fn as_char(&self) -> char {
self.0.ch as char
}
pub fn spacing(&self) -> Spacing {
if self.0.joint { Spacing::Joint } else { Spacing::Alone }
}
pub fn span(&self) -> Span {
Span(self.0.span)
}
pub fn set_span(&mut self, span: Span) {
self.0.span = span.0;
}
}
impl fmt::Display for Punct {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_char())
}
}
impl fmt::Debug for Punct {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Punct")
.field("ch", &self.as_char())
.field("spacing", &self.spacing())
.field("span", &self.span())
.finish()
}
}
impl PartialEq<char> for Punct {
fn eq(&self, rhs: &char) -> bool {
self.as_char() == *rhs
}
}
impl PartialEq<Punct> for char {
fn eq(&self, rhs: &Punct) -> bool {
*self == rhs.as_char()
}
}
#[derive(Clone)]
pub struct Ident(bridge::Ident<bridge::client::Span, bridge::client::Symbol>);
impl Ident {
pub fn new(string: &str, span: Span) -> Ident {
Ident(bridge::Ident {
sym: bridge::client::Symbol::new_ident(string, false),
is_raw: false,
span: span.0,
})
}
pub fn new_raw(string: &str, span: Span) -> Ident {
Ident(bridge::Ident {
sym: bridge::client::Symbol::new_ident(string, true),
is_raw: true,
span: span.0,
})
}
pub fn span(&self) -> Span {
Span(self.0.span)
}
pub fn set_span(&mut self, span: Span) {
self.0.span = span.0;
}
}
impl fmt::Display for Ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.0.is_raw {
f.write_str("r#")?;
}
fmt::Display::fmt(&self.0.sym, f)
}
}
impl fmt::Debug for Ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Ident")
.field("ident", &self.to_string())
.field("span", &self.span())
.finish()
}
}
#[derive(Clone)]
pub struct Literal(bridge::Literal<bridge::client::Span, bridge::client::Symbol>);
macro_rules! suffixed_int_literals {
($($name:ident => $kind:ident,)*) => ($(
pub fn $name(n: $kind) -> Literal {
Literal(bridge::Literal {
kind: bridge::LitKind::Integer,
symbol: bridge::client::Symbol::new(&n.to_string()),
suffix: Some(bridge::client::Symbol::new(stringify!($kind))),
span: Span::call_site().0,
})
}
)*)
}
macro_rules! unsuffixed_int_literals {
($($name:ident => $kind:ident,)*) => ($(
pub fn $name(n: $kind) -> Literal {
Literal(bridge::Literal {
kind: bridge::LitKind::Integer,
symbol: bridge::client::Symbol::new(&n.to_string()),
suffix: None,
span: Span::call_site().0,
})
}
)*)
}
macro_rules! integer_values {
($($nb:ident => $fn_name:ident,)+) => {
$(
#[doc = concat!(
"Returns the unescaped `",
stringify!($nb),
"` value if the literal is a `",
stringify!($nb),
"` or if it's an \"unmarked\" integer which doesn't overflow.")]
pub fn $fn_name(&self) -> Result<$nb, ConversionErrorKind> {
if self.0.kind != bridge::LitKind::Integer {
return Err(ConversionErrorKind::InvalidLiteralKind);
}
self.with_symbol_and_suffix(|symbol, suffix| {
match suffix {
stringify!($nb) | "" => {
let symbol = strip_underscores(symbol);
let (number, base) = parse_number(&symbol);
$nb::from_str_radix(&number, base as u32).map_err(|_| ConversionErrorKind::InvalidLiteralKind)
}
_ => Err(ConversionErrorKind::InvalidLiteralKind),
}
})
}
)+
}
}
macro_rules! float_values {
($($nb:ident => $fn_name:ident,)+) => {
$(
#[doc = concat!(
"Returns the unescaped `",
stringify!($nb),
"` value if the literal is a `",
stringify!($nb),
"` or if it's an \"unmarked\" float which doesn't overflow.")]
pub fn $fn_name(&self) -> Result<$nb, ConversionErrorKind> {
if self.0.kind != bridge::LitKind::Float {
return Err(ConversionErrorKind::InvalidLiteralKind);
}
self.with_symbol_and_suffix(|symbol, suffix| {
match suffix {
stringify!($nb) | "" => {
let number = strip_underscores(symbol);
$nb::from_str(&number).map_err(|_| ConversionErrorKind::InvalidLiteralKind)
}
_ => Err(ConversionErrorKind::InvalidLiteralKind),
}
})
}
)+
}
}
impl Literal {
fn new(kind: bridge::LitKind, value: &str, suffix: Option<&str>) -> Self {
Literal(bridge::Literal {
kind,
symbol: bridge::client::Symbol::new(value),
suffix: suffix.map(bridge::client::Symbol::new),
span: Span::call_site().0,
})
}
suffixed_int_literals! {
u8_suffixed => u8,
u16_suffixed => u16,
u32_suffixed => u32,
u64_suffixed => u64,
u128_suffixed => u128,
usize_suffixed => usize,
i8_suffixed => i8,
i16_suffixed => i16,
i32_suffixed => i32,
i64_suffixed => i64,
i128_suffixed => i128,
isize_suffixed => isize,
}
unsuffixed_int_literals! {
u8_unsuffixed => u8,
u16_unsuffixed => u16,
u32_unsuffixed => u32,
u64_unsuffixed => u64,
u128_unsuffixed => u128,
usize_unsuffixed => usize,
i8_unsuffixed => i8,
i16_unsuffixed => i16,
i32_unsuffixed => i32,
i64_unsuffixed => i64,
i128_unsuffixed => i128,
isize_unsuffixed => isize,
}
pub fn f32_unsuffixed(n: f32) -> Literal {
if !n.is_finite() {
panic!("Invalid float literal {n}");
}
let mut repr = n.to_string();
if !repr.contains('.') {
repr.push_str(".0");
}
Literal::new(bridge::LitKind::Float, &repr, None)
}
pub fn f32_suffixed(n: f32) -> Literal {
if !n.is_finite() {
panic!("Invalid float literal {n}");
}
Literal::new(bridge::LitKind::Float, &n.to_string(), Some("f32"))
}
pub fn f64_unsuffixed(n: f64) -> Literal {
if !n.is_finite() {
panic!("Invalid float literal {n}");
}
let mut repr = n.to_string();
if !repr.contains('.') {
repr.push_str(".0");
}
Literal::new(bridge::LitKind::Float, &repr, None)
}
pub fn f64_suffixed(n: f64) -> Literal {
if !n.is_finite() {
panic!("Invalid float literal {n}");
}
Literal::new(bridge::LitKind::Float, &n.to_string(), Some("f64"))
}
pub fn string(string: &str) -> Literal {
let escape = EscapeOptions {
escape_single_quote: false,
escape_double_quote: true,
escape_nonascii: false,
};
let repr = escape_bytes(string.as_bytes(), escape);
Literal::new(bridge::LitKind::Str, &repr, None)
}
pub fn character(ch: char) -> Literal {
let escape = EscapeOptions {
escape_single_quote: true,
escape_double_quote: false,
escape_nonascii: false,
};
let repr = escape_bytes(ch.encode_utf8(&mut [0u8; 4]).as_bytes(), escape);
Literal::new(bridge::LitKind::Char, &repr, None)
}
pub fn byte_character(byte: u8) -> Literal {
let escape = EscapeOptions {
escape_single_quote: true,
escape_double_quote: false,
escape_nonascii: true,
};
let repr = escape_bytes(&[byte], escape);
Literal::new(bridge::LitKind::Byte, &repr, None)
}
pub fn byte_string(bytes: &[u8]) -> Literal {
let escape = EscapeOptions {
escape_single_quote: false,
escape_double_quote: true,
escape_nonascii: true,
};
let repr = escape_bytes(bytes, escape);
Literal::new(bridge::LitKind::ByteStr, &repr, None)
}
pub fn c_string(string: &CStr) -> Literal {
let escape = EscapeOptions {
escape_single_quote: false,
escape_double_quote: true,
escape_nonascii: false,
};
let repr = escape_bytes(string.to_bytes(), escape);
Literal::new(bridge::LitKind::CStr, &repr, None)
}
pub fn span(&self) -> Span {
Span(self.0.span)
}
pub fn set_span(&mut self, span: Span) {
self.0.span = span.0;
}
pub fn subspan<R: RangeBounds<usize>>(&self, range: R) -> Option<Span> {
BridgeMethods::span_subspan(
self.0.span,
range.start_bound().cloned(),
range.end_bound().cloned(),
)
.map(Span)
}
fn with_symbol_and_suffix<R>(&self, f: impl FnOnce(&str, &str) -> R) -> R {
self.0.symbol.with(|symbol| match self.0.suffix {
Some(suffix) => suffix.with(|suffix| f(symbol, suffix)),
None => f(symbol, ""),
})
}
fn with_stringify_parts<R>(&self, f: impl FnOnce(&[&str]) -> R) -> R {
fn get_hashes_str(num: u8) -> &'static str {
const HASHES: &str = "\
################################################################\
################################################################\
################################################################\
################################################################\
";
const _: () = assert!(HASHES.len() == 256);
&HASHES[..num as usize]
}
self.with_symbol_and_suffix(|symbol, suffix| match self.0.kind {
bridge::LitKind::Byte => f(&["b'", symbol, "'", suffix]),
bridge::LitKind::Char => f(&["'", symbol, "'", suffix]),
bridge::LitKind::Str => f(&["\"", symbol, "\"", suffix]),
bridge::LitKind::StrRaw(n) => {
let hashes = get_hashes_str(n);
f(&["r", hashes, "\"", symbol, "\"", hashes, suffix])
}
bridge::LitKind::ByteStr => f(&["b\"", symbol, "\"", suffix]),
bridge::LitKind::ByteStrRaw(n) => {
let hashes = get_hashes_str(n);
f(&["br", hashes, "\"", symbol, "\"", hashes, suffix])
}
bridge::LitKind::CStr => f(&["c\"", symbol, "\"", suffix]),
bridge::LitKind::CStrRaw(n) => {
let hashes = get_hashes_str(n);
f(&["cr", hashes, "\"", symbol, "\"", hashes, suffix])
}
bridge::LitKind::Integer | bridge::LitKind::Float | bridge::LitKind::ErrWithGuar => {
f(&[symbol, suffix])
}
})
}
pub fn byte_character_value(&self) -> Result<u8, ConversionErrorKind> {
self.0.symbol.with(|symbol| match self.0.kind {
bridge::LitKind::Byte => unescape_byte(symbol)
.map_err(|err| ConversionErrorKind::FailedToUnescape(err.into())),
_ => Err(ConversionErrorKind::InvalidLiteralKind),
})
}
pub fn character_value(&self) -> Result<char, ConversionErrorKind> {
self.0.symbol.with(|symbol| match self.0.kind {
bridge::LitKind::Char => unescape_char(symbol)
.map_err(|err| ConversionErrorKind::FailedToUnescape(err.into())),
_ => Err(ConversionErrorKind::InvalidLiteralKind),
})
}
pub fn str_value(&self) -> Result<String, ConversionErrorKind> {
self.0.symbol.with(|symbol| match self.0.kind {
bridge::LitKind::Str => {
if symbol.contains('\\') {
let mut buf = String::with_capacity(symbol.len());
let mut error = None;
unescape_str(
symbol,
|_, c| match c {
Ok(c) => buf.push(c),
Err(err) => {
if err.is_fatal() {
error = Some(ConversionErrorKind::FailedToUnescape(err.into()));
}
}
},
);
if let Some(error) = error { Err(error) } else { Ok(buf) }
} else {
Ok(symbol.to_string())
}
}
bridge::LitKind::StrRaw(_) => Ok(symbol.to_string()),
_ => Err(ConversionErrorKind::InvalidLiteralKind),
})
}
pub fn cstr_value(&self) -> Result<Vec<u8>, ConversionErrorKind> {
self.0.symbol.with(|symbol| match self.0.kind {
bridge::LitKind::CStr => {
let mut error = None;
let mut buf = Vec::with_capacity(symbol.len());
unescape_c_str(symbol, |_span, res| match res {
Ok(MixedUnit::Char(c)) => {
buf.extend_from_slice(c.get().encode_utf8(&mut [0; 4]).as_bytes())
}
Ok(MixedUnit::HighByte(b)) => buf.push(b.get()),
Err(err) => {
if err.is_fatal() {
error = Some(ConversionErrorKind::FailedToUnescape(err.into()));
}
}
});
if let Some(error) = error {
Err(error)
} else {
buf.push(0);
Ok(buf)
}
}
bridge::LitKind::CStrRaw(_) => {
let mut buf = symbol.to_owned().into_bytes();
buf.push(0);
Ok(buf)
}
_ => Err(ConversionErrorKind::InvalidLiteralKind),
})
}
pub fn byte_str_value(&self) -> Result<Vec<u8>, ConversionErrorKind> {
self.0.symbol.with(|symbol| match self.0.kind {
bridge::LitKind::ByteStr => {
let mut buf = Vec::with_capacity(symbol.len());
let mut error = None;
unescape_byte_str(symbol, |_, res| match res {
Ok(b) => buf.push(b),
Err(err) => {
if err.is_fatal() {
error = Some(ConversionErrorKind::FailedToUnescape(err.into()));
}
}
});
if let Some(error) = error { Err(error) } else { Ok(buf) }
}
bridge::LitKind::ByteStrRaw(_) => {
Ok(symbol.to_owned().into_bytes())
}
_ => Err(ConversionErrorKind::InvalidLiteralKind),
})
}
integer_values! {
u8 => u8_value,
u16 => u16_value,
u32 => u32_value,
u64 => u64_value,
u128 => u128_value,
i8 => i8_value,
i16 => i16_value,
i32 => i32_value,
i64 => i64_value,
i128 => i128_value,
}
float_values! {
f32 => f32_value,
f64 => f64_value,
}
}
#[repr(u32)]
#[derive(PartialEq, Eq)]
enum Base {
Decimal = 10,
Binary = 2,
Octal = 8,
Hexadecimal = 16,
}
fn parse_number(value: &str) -> (&str, Base) {
let mut iter = value.as_bytes().iter().copied();
let Some(first_digit) = iter.next() else {
return ("0", Base::Decimal);
};
let Some(second_digit) = iter.next() else {
return (value, Base::Decimal);
};
let mut base = Base::Decimal;
if first_digit == b'0' {
match second_digit {
b'b' => {
base = Base::Binary;
}
b'o' => {
base = Base::Octal;
}
b'x' => {
base = Base::Hexadecimal;
}
_ => {}
}
}
let offset = if base == Base::Decimal { 0 } else { 2 };
(&value[offset..], base)
}
fn strip_underscores(value_s: &str) -> Cow<'_, str> {
let value = value_s.as_bytes();
if value.iter().copied().all(|c| c != b'_' && c != b'f') {
return Cow::Borrowed(value_s);
}
let mut output = String::with_capacity(value.len());
for c in value.iter().copied() {
if c != b'_' {
output.push(c as char);
}
}
Cow::Owned(output)
}
impl FromStr for Literal {
type Err = LexError;
fn from_str(src: &str) -> Result<Self, LexError> {
match BridgeMethods::literal_from_str(src) {
Ok(literal) => Ok(Literal(literal)),
Err(msg) => Err(LexError(msg, PhantomData)),
}
}
}
impl fmt::Display for Literal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.with_stringify_parts(|parts| {
for part in parts {
fmt::Display::fmt(part, f)?;
}
Ok(())
})
}
}
impl fmt::Debug for Literal {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Literal")
.field("kind", &format_args!("{:?}", self.0.kind))
.field("symbol", &self.0.symbol)
.field("suffix", &format_args!("{:?}", self.0.suffix))
.field("span", &self.0.span)
.finish()
}
}
pub mod tracked {
use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
use eko::env;
use eko::path::Path;
use crate::rustc_proc_macro::BridgeMethods;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum VarError {
NotPresent,
NotUnicode(Vec<u8>),
}
impl fmt::Display for VarError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
VarError::NotPresent => f.write_str("environment variable not found"),
VarError::NotUnicode(_) => {
f.write_str("environment variable was not valid unicode")
}
}
}
}
impl core::error::Error for VarError {}
pub fn env_var<K: AsRef<str>>(key: K) -> Result<String, VarError> {
let key: &str = key.as_ref();
let value = match BridgeMethods::injected_env_var(key) {
Some(injected) => Ok(injected),
None => match env::var_os(key) {
None => Err(VarError::NotPresent),
Some(bytes) => {
String::from_utf8(bytes).map_err(|e| VarError::NotUnicode(e.into_bytes()))
}
},
};
BridgeMethods::track_env_var(key, value.as_deref().ok());
value
}
pub fn path<P: AsRef<Path>>(path: P) {
let path: &str = path.as_ref().to_str().unwrap();
BridgeMethods::track_path(path);
}
}