use std::marker::PhantomData;
use crate::{
common::{ExpnId, MacroId, SpanId, SpanSrcId, SymbolId},
context::with_cx,
diagnostic::Applicability,
ffi,
private::Sealed,
};
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct SpanPos(
).
u32,
);
#[cfg(feature = "driver-api")]
impl SpanPos {
pub fn new(index: u32) -> Self {
Self(index)
}
pub fn index(self) -> u32 {
self.0
}
}
#[repr(C)]
#[derive(Debug)]
pub struct ExpnInfo<'ast> {
_lifetime: PhantomData<&'ast ()>,
parent: ExpnId,
call_site: SpanId,
macro_id: MacroId,
}
impl<'ast> ExpnInfo<'ast> {
#[must_use]
pub fn parent(&self) -> Option<&ExpnInfo<'ast>> {
with_cx(self, |cx| cx.span_expn_info(self.parent))
}
#[must_use]
pub fn call_site(&self) -> &Span<'ast> {
with_cx(self, |cx| cx.span(self.call_site))
}
pub fn macro_id(&self) -> MacroId {
self.macro_id
}
}
#[cfg(feature = "driver-api")]
impl<'ast> ExpnInfo<'ast> {
#[must_use]
pub fn new(parent: ExpnId, call_site: SpanId, macro_id: MacroId) -> Self {
Self {
_lifetime: PhantomData,
parent,
call_site,
macro_id,
}
}
}
#[repr(C)]
#[derive(Clone)]
pub struct Span<'ast> {
_lifetime: PhantomData<&'ast ()>,
source_id: SpanSrcId,
from_expansion: bool,
start: SpanPos,
end: SpanPos,
}
impl<'ast> std::fmt::Debug for Span<'ast> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fn fmt_pos(pos: Option<FilePos<'_>>) -> String {
match pos {
Some(pos) => format!("{}:{}", pos.line(), pos.column()),
None => "[invalid]".to_string(),
}
}
let src = self.source();
let name = match src {
SpanSource::File(file) => format!(
"{}:{} - {}",
file.file(),
fmt_pos(file.try_to_file_pos(self.start)),
fmt_pos(file.try_to_file_pos(self.end))
),
SpanSource::Macro(expn) => format!("[Inside Macro] {:#?}", expn.call_site()),
SpanSource::Builtin(_) => "[Builtin]".to_string(),
};
f.debug_struct(&name).finish()
}
}
impl<'ast> Span<'ast> {
pub fn is_from_expansion(&self) -> bool {
self.from_expansion
}
#[must_use]
pub fn snippet(&self) -> Option<&'ast str> {
with_cx(self, |cx| cx.span_snipped(self))
}
pub fn snippet_or<'a, 'b>(&self, default: &'a str) -> &'b str
where
'a: 'b,
'ast: 'b,
{
self.snippet().unwrap_or(default)
}
pub fn snippet_with_applicability<'a, 'b>(&self, placeholder: &'a str, applicability: &mut Applicability) -> &'b str
where
'a: 'b,
'ast: 'b,
{
if *applicability != Applicability::Unspecified && self.is_from_expansion() {
*applicability = Applicability::MaybeIncorrect;
}
self.snippet().unwrap_or_else(|| {
if matches!(
*applicability,
Applicability::MachineApplicable | Applicability::MaybeIncorrect
) {
*applicability = Applicability::HasPlaceholders;
}
placeholder
})
}
pub fn len(&self) -> usize {
(self.end.0 - self.start.0)
.try_into()
.expect("Marker is not compiled for usize::BITs < 32")
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn start(&self) -> SpanPos {
self.start
}
pub fn set_start(&mut self, start: SpanPos) {
assert!(
start.0 <= self.end.0,
"the start position should always be <= of the end position"
);
self.start = start;
}
#[must_use]
pub fn with_start(&self, start: SpanPos) -> Span<'ast> {
let mut new_span = self.clone();
new_span.set_start(start);
new_span
}
pub fn end(&self) -> SpanPos {
self.end
}
pub fn set_end(&mut self, end: SpanPos) {
assert!(
self.start.0 <= end.0,
"the start position should always be >= of the end position"
);
self.end = end;
}
#[must_use]
pub fn with_end(&self, end: SpanPos) -> Span<'ast> {
let mut new_span = self.clone();
new_span.set_end(end);
new_span
}
#[must_use]
pub fn source(&self) -> SpanSource<'ast> {
with_cx(self, |cx| cx.span_source(self))
}
}
impl<'ast> HasSpan<'ast> for Span<'ast> {
fn span(&self) -> &Span<'ast> {
self
}
}
#[cfg(feature = "driver-api")]
impl<'ast> Span<'ast> {
#[must_use]
pub fn new(source_id: SpanSrcId, from_expansion: bool, start: SpanPos, end: SpanPos) -> Self {
Self {
_lifetime: PhantomData,
source_id,
from_expansion,
start,
end,
}
}
pub fn source_id(&self) -> SpanSrcId {
self.source_id
}
}
#[repr(C)]
#[derive(Debug)]
#[non_exhaustive]
pub enum SpanSource<'ast> {
File(&'ast FileInfo<'ast>),
Macro(&'ast ExpnInfo<'ast>),
Builtin(&'ast BuiltinInfo<'ast>),
}
#[repr(C)]
#[derive(Debug)]
pub struct FileInfo<'ast> {
file: ffi::FfiStr<'ast>,
span_src: SpanSrcId,
}
impl<'ast> FileInfo<'ast> {
pub fn file(&self) -> &str {
self.file.get()
}
pub fn try_to_file_pos(&self, span_pos: SpanPos) -> Option<FilePos> {
with_cx(self, |cx| cx.span_pos_to_file_loc(self, span_pos))
}
pub fn to_file_pos(&self, span_pos: SpanPos) -> FilePos {
self.try_to_file_pos(span_pos).unwrap_or_else(|| {
panic!(
"the given span position `{span_pos:#?}` is out of range of the file `{}`",
self.file.get()
)
})
}
}
#[cfg(feature = "driver-api")]
impl<'ast> FileInfo<'ast> {
#[must_use]
pub fn new(file: &'ast str, span_src: SpanSrcId) -> Self {
Self {
file: file.into(),
span_src,
}
}
pub fn span_src(&self) -> SpanSrcId {
self.span_src
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct FilePos<'ast> {
_lifetime: PhantomData<&'ast ()>,
line: usize,
column: usize,
}
impl<'ast> FilePos<'ast> {
pub fn line(&self) -> usize {
self.line
}
pub fn column(&self) -> usize {
self.column
}
}
#[cfg(feature = "driver-api")]
impl<'ast> FilePos<'ast> {
pub fn new(line: usize, column: usize) -> Self {
Self {
_lifetime: PhantomData,
line,
column,
}
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "driver-api", derive(Default))]
pub struct BuiltinInfo<'ast> {
_lifetime: PhantomData<&'ast ()>,
_data: u8,
}
#[repr(C)]
#[cfg_attr(feature = "driver-api", derive(Clone))]
pub struct Ident<'ast> {
_lifetime: PhantomData<&'ast ()>,
sym: SymbolId,
span: SpanId,
}
impl<'ast> Ident<'ast> {
pub fn name(&self) -> &str {
with_cx(self, |cx| cx.symbol_str(self.sym))
}
}
impl<'ast> HasSpan<'ast> for Ident<'ast> {
fn span(&self) -> &Span<'ast> {
with_cx(self, |cx| cx.span(self.span))
}
}
#[cfg(feature = "driver-api")]
impl<'ast> Ident<'ast> {
pub fn new(sym: SymbolId, span: SpanId) -> Self {
Self {
_lifetime: PhantomData,
sym,
span,
}
}
}
impl<'ast> std::fmt::Debug for Ident<'ast> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Ident")
.field("name", &self.name())
.field("span", &self.span())
.finish()
}
}
impl<'ast> std::fmt::Display for Ident<'ast> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.name())
}
}
macro_rules! impl_ident_eq_for {
($ty:ty) => {
impl<'ast> PartialEq<$ty> for Ident<'ast> {
fn eq(&self, other: &$ty) -> bool {
self.name().eq(other)
}
}
impl<'ast> PartialEq<Ident<'ast>> for $ty {
fn eq(&self, other: &Ident<'ast>) -> bool {
other.name().eq(self)
}
}
};
($($ty:ty),+) => {
$(
impl_ident_eq_for!($ty);
)+
};
}
use impl_ident_eq_for;
impl_ident_eq_for!(
str,
String,
std::ffi::OsStr,
std::ffi::OsString,
std::borrow::Cow<'_, str>
);
pub trait HasSpan<'ast>: Sealed {
fn span(&self) -> &Span<'ast>;
}
macro_rules! impl_has_span_via_field {
($ty:ty) => {
$crate::span::impl_has_span_via_field!($ty, span);
};
($ty:ty, $($field_access:ident).*) => {
impl<'ast> $crate::span::HasSpan<'ast> for $ty {
fn span(&self) -> &$crate::span::Span<'ast> {
$crate::context::with_cx(self, |cx| cx.span(self.$($field_access).*))
}
}
}
}
pub(crate) use impl_has_span_via_field;
macro_rules! impl_spanned_for {
($ty:ty) => {
impl<'ast> $crate::span::HasSpan<'ast> for $ty {
fn span(&self) -> &$crate::span::Span<'ast> {
self.span()
}
}
};
}
pub(crate) use impl_spanned_for;
impl<'ast, N: HasSpan<'ast>> HasSpan<'ast> for &N {
fn span(&self) -> &Span<'ast> {
(*self).span()
}
}