use std::{cell::RefCell, mem::transmute};
use crate::{
common::{ExpnId, ExprId, ItemId, Level, MacroReport, SpanId, SymbolId, TyDefId},
diagnostic::{Diagnostic, DiagnosticBuilder, EmissionNode},
ffi,
sem::TyKind,
span::{ExpnInfo, FileInfo, FilePos, Span, SpanPos, SpanSource},
Lint,
};
mod map;
pub use map::*;
thread_local! {
#[doc(hidden)]
static AST_CX: RefCell<Option<&'static MarkerContext<'static>>> = RefCell::new(None);
}
#[doc(hidden)]
pub fn set_ast_cx<'ast>(cx: &'ast MarkerContext<'ast>) {
let cx_static: &'static MarkerContext<'static> = unsafe { transmute(cx) };
AST_CX.with(|cx| cx.replace(Some(cx_static)));
}
pub(crate) fn with_cx<'src, 'ast: 'src, T, F, R>(_lifetime_src: &'src T, f: F) -> R
where
F: FnOnce(&'src MarkerContext<'ast>) -> R,
'static: 'src,
{
AST_CX.with(|cx| {
let cx_static: &'static MarkerContext<'static> = cx
.borrow()
.expect("`with_cx` should only be called by nodes once the context has been set");
let cx_ast: &'src MarkerContext<'ast> = unsafe { transmute(cx_static) };
f(cx_ast)
})
}
#[repr(C)]
#[cfg_attr(feature = "driver-api", derive(typed_builder::TypedBuilder))]
pub struct MarkerContext<'ast> {
ast: AstMap<'ast>,
callbacks: MarkerContextCallbacks<'ast>,
}
impl<'ast> std::fmt::Debug for MarkerContext<'ast> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MarkerContext").finish()
}
}
impl<'ast> MarkerContext<'ast> {
pub fn ast(&self) -> &AstMap<'ast> {
&self.ast
}
pub fn emit_lint(
&self,
lint: &'static Lint,
node: impl EmissionNode<'ast>,
msg: impl Into<String>,
) -> DiagnosticBuilder<'ast> {
let id = node.node_id();
let span = node.span();
if matches!(lint.report_in_macro, MacroReport::No) && span.is_from_expansion() {
return DiagnosticBuilder::dummy();
}
if self.ast().lint_level_at(lint, &node) == Level::Allow {
return DiagnosticBuilder::dummy();
}
DiagnosticBuilder::new(lint, id, msg.into(), span.clone())
}
pub(crate) fn emit_diagnostic<'a>(&self, diag: &'a Diagnostic<'a, 'ast>) {
self.callbacks.call_emit_diagnostic(diag);
}
pub fn resolve_ty_ids(&self, path: &str) -> &[TyDefId] {
(self.callbacks.resolve_ty_ids)(self.callbacks.data, path.into()).get()
}
}
impl<'ast> MarkerContext<'ast> {
pub(crate) fn expr_ty(&self, expr: ExprId) -> TyKind<'ast> {
self.callbacks.call_expr_ty(expr)
}
pub(crate) fn span_snipped(&self, span: &Span<'ast>) -> Option<&'ast str> {
(self.callbacks.span_snippet)(self.callbacks.data, span)
.get()
.map(ffi::FfiStr::get)
}
pub(crate) fn span(&self, span_id: SpanId) -> &'ast Span<'ast> {
self.callbacks.call_span(span_id)
}
pub(crate) fn span_source(&self, span: &Span<'_>) -> SpanSource<'ast> {
(self.callbacks.span_source)(self.callbacks.data, span)
}
pub(crate) fn span_pos_to_file_loc(&self, file: &FileInfo<'ast>, pos: SpanPos) -> Option<FilePos<'ast>> {
(self.callbacks.span_pos_to_file_loc)(self.callbacks.data, file, pos).into()
}
pub(crate) fn span_expn_info(&self, src_id: ExpnId) -> Option<&'ast ExpnInfo<'ast>> {
(self.callbacks.span_expn_info)(self.callbacks.data, src_id).into()
}
pub(crate) fn symbol_str(&self, sym: SymbolId) -> &'ast str {
self.callbacks.call_symbol_str(sym)
}
#[allow(unused)] pub(crate) fn resolve_method_target(&self, expr: ExprId) -> ItemId {
self.callbacks.resolve_method_target(expr)
}
}
#[repr(C)]
#[cfg_attr(feature = "driver-api", visibility::make(pub))]
struct MarkerContextCallbacks<'ast> {
pub data: &'ast MarkerContextData,
pub emit_diag: for<'a> extern "C" fn(&'ast MarkerContextData, &'a Diagnostic<'a, 'ast>),
pub resolve_ty_ids: extern "C" fn(&'ast MarkerContextData, path: ffi::FfiStr<'_>) -> ffi::FfiSlice<'ast, TyDefId>,
pub expr_ty: extern "C" fn(&'ast MarkerContextData, ExprId) -> TyKind<'ast>,
pub span: extern "C" fn(&'ast MarkerContextData, SpanId) -> &'ast Span<'ast>,
pub span_snippet: extern "C" fn(&'ast MarkerContextData, &Span<'ast>) -> ffi::FfiOption<ffi::FfiStr<'ast>>,
pub span_source: extern "C" fn(&'ast MarkerContextData, &Span<'_>) -> SpanSource<'ast>,
pub span_pos_to_file_loc:
extern "C" fn(&'ast MarkerContextData, &FileInfo<'ast>, SpanPos) -> ffi::FfiOption<FilePos<'ast>>,
pub span_expn_info: extern "C" fn(&'ast MarkerContextData, ExpnId) -> ffi::FfiOption<&'ast ExpnInfo<'ast>>,
pub symbol_str: extern "C" fn(&'ast MarkerContextData, SymbolId) -> ffi::FfiStr<'ast>,
pub resolve_method_target: extern "C" fn(&'ast MarkerContextData, ExprId) -> ItemId,
}
impl<'ast> MarkerContextCallbacks<'ast> {
fn call_emit_diagnostic<'a>(&self, diag: &'a Diagnostic<'a, 'ast>) {
(self.emit_diag)(self.data, diag);
}
fn call_expr_ty(&self, expr: ExprId) -> TyKind<'ast> {
(self.expr_ty)(self.data, expr)
}
fn call_span(&self, span_id: SpanId) -> &'ast Span<'ast> {
(self.span)(self.data, span_id)
}
fn call_symbol_str(&self, sym: SymbolId) -> &'ast str {
(self.symbol_str)(self.data, sym).get()
}
pub fn resolve_method_target(&self, expr: ExprId) -> ItemId {
(self.resolve_method_target)(self.data, expr)
}
}
#[repr(C)]
#[cfg_attr(feature = "driver-api", visibility::make(pub))]
struct MarkerContextData {
_data: usize,
}