use smallvec::SmallVec;
use std::mem;
use syntax::ast::*;
use syntax::mut_visit::{self, MutVisitor};
use syntax::ptr::P;
use syntax::source_map::{Span, DUMMY_SP};
use syntax_pos::hygiene::SyntaxContext;
use crate::ast_manip::util::extended_span;
use crate::ast_manip::MutVisit;
struct FixFormat {
parent_span: Span,
in_format: bool,
}
impl FixFormat {
fn descend<F, R>(&mut self, in_format: bool, cur_span: Span, f: F) -> R
where
F: FnOnce(&mut Self) -> R,
{
let old_in_format = mem::replace(&mut self.in_format, in_format);
let old_parent_span = mem::replace(&mut self.parent_span, cur_span);
let r = f(self);
self.in_format = old_in_format;
self.parent_span = old_parent_span;
r
}
fn is_format_entry(&self, e: &Expr) -> bool {
if !matches!([e.node] ExprKind::Match(..)) {
return false;
}
if e.span.ctxt() == SyntaxContext::empty() {
return false;
}
e.span.source_callsite().contains(e.span)
}
}
impl MutVisitor for FixFormat {
fn visit_expr(&mut self, e: &mut P<Expr>) {
if self.in_format
&& e.span.ctxt() == SyntaxContext::empty()
&& matches!([e.node] ExprKind::AddrOf(..))
{
trace!("EXITING format! at {:?}", e);
let new_span = self.parent_span;
self.descend(false, e.span, |this| {
mut_visit::noop_visit_expr(e, this);
e.span = new_span;
})
} else if !self.in_format && self.is_format_entry(&e) {
trace!("ENTERING format! at {:?}", e);
self.descend(true, e.span, |this| mut_visit::noop_visit_expr(e, this))
} else {
let in_format = self.in_format;
self.descend(in_format, e.span, |this| {
mut_visit::noop_visit_expr(e, this)
})
}
}
fn visit_mac(&mut self, mac: &mut Mac) {
mut_visit::noop_visit_mac(mac, self)
}
}
struct FixAttrs;
impl MutVisitor for FixAttrs {
fn flat_map_item(&mut self, i: P<Item>) -> SmallVec<[P<Item>; 1]> {
let new_span = extended_span(i.span, &i.attrs);
let i = if new_span != i.span {
i.map(|i| Item {
span: new_span,
..i
})
} else {
i
};
mut_visit::noop_flat_map_item(i, self)
}
fn flat_map_foreign_item(&mut self, fi: ForeignItem) -> SmallVec<[ForeignItem; 1]> {
let new_span = extended_span(fi.span, &fi.attrs);
let fi = if new_span != fi.span {
ForeignItem {
span: new_span,
..fi
}
} else {
fi
};
mut_visit::noop_flat_map_foreign_item(fi, self)
}
fn visit_mac(&mut self, mac: &mut Mac) {
mut_visit::noop_visit_mac(mac, self)
}
}
pub fn fix_format<T: MutVisit>(node: &mut T) {
let mut fix_format = FixFormat {
parent_span: DUMMY_SP,
in_format: false,
};
node.visit(&mut fix_format)
}
pub fn fix_attr_spans<T: MutVisit>(node: &mut T) {
node.visit(&mut FixAttrs)
}