use rustc::hir::def_id::DefId;
use rustc::session::Session;
use smallvec::SmallVec;
use std::cmp;
use std::result;
use syntax::ast::{Block, Expr, ExprKind, Ident, Item, Label, Pat, Path, Stmt, Ty};
use syntax::mut_visit::{self, MutVisitor};
use syntax::parse::parser::{Parser, PathStyle};
use syntax::parse::token::Token;
use syntax::parse::{self, PResult};
use syntax::ptr::P;
use syntax::symbol::Symbol;
use syntax::tokenstream::TokenStream;
use syntax_pos::FileName;
use crate::ast_manip::util::PatternSymbol;
use crate::ast_manip::{remove_paren, GetNodeId, MutVisit};
use crate::command::CommandState;
use crate::driver::{self, emit_and_panic};
use crate::reflect;
use crate::RefactorCtxt;
use c2rust_ast_builder::IntoSymbol;
mod bindings;
mod impls;
mod subst;
pub use self::bindings::{parse_bindings, BindingTypes, Bindings, Type as BindingType};
pub use self::subst::Subst;
pub type Result<T> = result::Result<T, Error>;
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Error {
VariantMismatch,
LengthMismatch,
SymbolMismatch,
NonlinearMismatch,
NotMarked,
DefMismatch,
WrongType,
TypeUnavailable,
BadSpecialPattern(Symbol),
}
#[derive(Clone)]
pub struct MatchCtxt<'a, 'tcx: 'a> {
pub bindings: Bindings,
pub types: BindingTypes,
st: &'a CommandState,
cx: &'a RefactorCtxt<'a, 'tcx>,
pub debug: bool,
}
impl<'a, 'tcx> MatchCtxt<'a, 'tcx> {
pub fn new(st: &'a CommandState, cx: &'a RefactorCtxt<'a, 'tcx>) -> MatchCtxt<'a, 'tcx> {
MatchCtxt {
bindings: Bindings::new(),
types: BindingTypes::new(),
st: st,
cx: cx,
debug: false,
}
}
pub fn parse_expr(&mut self, src: &str) -> P<Expr> {
let (mut p, bt) = make_bindings_parser(self.cx.session(), src);
match p.parse_expr() {
Ok(mut expr) => {
self.types.merge(bt);
remove_paren(&mut expr);
expr
}
Err(db) => emit_and_panic(db, "expr"),
}
}
pub fn parse_pat(&mut self, src: &str) -> P<Pat> {
let (mut p, bt) = make_bindings_parser(self.cx.session(), src);
match p.parse_pat(None) {
Ok(mut pat) => {
self.types.merge(bt);
remove_paren(&mut pat);
pat
}
Err(db) => emit_and_panic(db, "pat"),
}
}
pub fn parse_ty(&mut self, src: &str) -> P<Ty> {
let (mut p, bt) = make_bindings_parser(self.cx.session(), src);
match p.parse_ty() {
Ok(mut ty) => {
self.types.merge(bt);
remove_paren(&mut ty);
ty
}
Err(db) => emit_and_panic(db, "ty"),
}
}
pub fn parse_stmts(&mut self, src: &str) -> Vec<Stmt> {
let (mut p, bt) = make_bindings_parser(self.cx.session(), &format!("{{ {} }}", src));
match p.parse_block() {
Ok(blk) => {
self.types.merge(bt);
let mut stmts = blk.into_inner().stmts;
for s in stmts.iter_mut() {
remove_paren(s);
}
stmts
}
Err(db) => emit_and_panic(db, "stmts"),
}
}
pub fn parse_items(&mut self, src: &str) -> Vec<P<Item>> {
let (mut p, bt) = make_bindings_parser(self.cx.session(), src);
let mut items = Vec::new();
loop {
match p.parse_item() {
Ok(Some(mut item)) => {
remove_paren(&mut item);
items.push(item);
}
Ok(None) => break,
Err(db) => emit_and_panic(db, "items"),
}
}
self.types.merge(bt);
items
}
pub fn try_match<T: TryMatch>(&mut self, pat: &T, target: &T) -> Result<()> {
let r = pat.try_match(target, self);
r
}
pub fn from_match<T: TryMatch>(
st: &'a CommandState,
cx: &'a RefactorCtxt<'a, 'tcx>,
pat: &T,
target: &T,
) -> Result<MatchCtxt<'a, 'tcx>> {
let mut m = MatchCtxt::new(st, cx);
m.try_match(pat, target)?;
Ok(m)
}
pub fn clone_match<T: TryMatch>(&self, pat: &T, target: &T) -> Result<MatchCtxt<'a, 'tcx>> {
let mut m = self.clone();
m.try_match(pat, target)?;
Ok(m)
}
pub fn set_type<S: IntoSymbol>(&mut self, name: S, ty: BindingType) {
let name = name.into_symbol();
if let Some(old_ty) = self.bindings.get_type(name) {
assert!(
ty == old_ty || ty == BindingType::Unknown || old_ty == BindingType::Unknown,
"tried to set type of {:?} to {:?}, but it already has a value of type {:?}",
name,
ty,
old_ty
);
}
self.types.set_type(name, ty)
}
fn is_opt_binding<P: PatternSymbol>(&self, pattern: &P) -> bool {
let sym = match pattern.pattern_symbol() {
Some(x) => x,
None => return false,
};
match self.types.get(&sym) {
Some(&bindings::Type::Optional(_)) => true,
_ => false,
}
}
fn capture_opt_none<P: PatternSymbol>(&mut self, pattern: &P) -> Result<()> {
let sym = match pattern.pattern_symbol() {
Some(x) => x,
None => panic!("should never reach this"),
};
match self.types.get(&sym) {
Some(&bindings::Type::Optional(_)) => {
let ok = self.bindings.try_add_none(sym);
if ok {
Ok(())
} else {
Err(Error::NonlinearMismatch)
}
}
bt @ _ => panic!("expected optional binding, got {:?}", bt),
}
}
pub fn maybe_capture_ident(&mut self, pattern: &Ident, target: &Ident) -> Result<bool> {
let sym = match pattern.pattern_symbol() {
Some(x) => x,
None => return Ok(false),
};
match self.types.get(&sym) {
Some(&bindings::Type::Optional(bindings::Type::Ident)) => {
let ok = self.bindings.try_add(sym, Some(target.clone()));
let res = if ok {
Ok(true)
} else {
Err(Error::NonlinearMismatch)
};
return res;
}
Some(&bindings::Type::Ident) => {}
Some(&bindings::Type::Unknown) => {}
None if sym.as_str().starts_with("__") => {}
_ => return Ok(false),
}
let ok = self.bindings.try_add(sym, target.clone());
if ok {
Ok(true)
} else {
Err(Error::NonlinearMismatch)
}
}
pub fn maybe_capture_label(&mut self, pattern: &Label, target: &Label) -> Result<bool> {
let sym = match pattern.pattern_symbol() {
Some(x) => x,
None => return Ok(false),
};
match self.types.get(&sym) {
Some(&bindings::Type::Optional(bindings::Type::Ident)) => {
let ok = self.bindings.try_add(sym, Some(target.ident.clone()));
let res = if ok {
Ok(true)
} else {
Err(Error::NonlinearMismatch)
};
return res;
}
Some(&bindings::Type::Ident) => {}
Some(&bindings::Type::Unknown) => {}
None if sym.as_str().starts_with("'__") => {}
_ => return Ok(false),
}
let ok = self.bindings.try_add(sym, target.ident.clone());
if ok {
Ok(true)
} else {
Err(Error::NonlinearMismatch)
}
}
pub fn maybe_capture_path(&mut self, pattern: &Path, target: &Path) -> Result<bool> {
let sym = match pattern.pattern_symbol() {
Some(x) => x,
None => return Ok(false),
};
match self.types.get(&sym) {
Some(&bindings::Type::Path) => {}
Some(&bindings::Type::Unknown) => {}
None if sym.as_str().starts_with("__") => {}
_ => return Ok(false),
}
let ok = self.bindings.try_add(sym, target.clone());
if ok {
Ok(true)
} else {
Err(Error::NonlinearMismatch)
}
}
pub fn maybe_capture_expr(&mut self, pattern: &Expr, target: &Expr) -> Result<bool> {
let sym = match pattern.pattern_symbol() {
Some(x) => x,
None => return Ok(false),
};
match self.types.get(&sym) {
Some(&bindings::Type::Optional(bindings::Type::Expr)) => {
let ok = self.bindings.try_add(sym, Some(P(target.clone())));
let res = if ok {
Ok(true)
} else {
Err(Error::NonlinearMismatch)
};
return res;
}
Some(&bindings::Type::Expr) => {}
Some(&bindings::Type::Unknown) => {}
None if sym.as_str().starts_with("__") => {}
_ => return Ok(false),
}
let ok = self.bindings.try_add(sym, P(target.clone()));
if ok {
Ok(true)
} else {
Err(Error::NonlinearMismatch)
}
}
pub fn maybe_capture_pat(&mut self, pattern: &Pat, target: &Pat) -> Result<bool> {
let sym = match pattern.pattern_symbol() {
Some(x) => x,
None => return Ok(false),
};
match self.types.get(&sym) {
Some(&bindings::Type::Pat) => {}
Some(&bindings::Type::Unknown) => {}
None if sym.as_str().starts_with("__") => {}
_ => return Ok(false),
}
let ok = self.bindings.try_add(sym, P(target.clone()));
if ok {
Ok(true)
} else {
Err(Error::NonlinearMismatch)
}
}
pub fn maybe_capture_ty(&mut self, pattern: &Ty, target: &Ty) -> Result<bool> {
let sym = match pattern.pattern_symbol() {
Some(x) => x,
None => return Ok(false),
};
match self.types.get(&sym) {
Some(&bindings::Type::Optional(bindings::Type::Ty)) => {
let ok = self.bindings.try_add(sym, Some(P(target.clone())));
let res = if ok {
Ok(true)
} else {
Err(Error::NonlinearMismatch)
};
return res;
}
Some(&bindings::Type::Ty) => {}
Some(&bindings::Type::Unknown) => {}
None if sym.as_str().starts_with("__") => {}
_ => return Ok(false),
}
let ok = self.bindings.try_add(sym, P(target.clone()));
if ok {
Ok(true)
} else {
Err(Error::NonlinearMismatch)
}
}
pub fn maybe_capture_stmt(&mut self, pattern: &Stmt, target: &Stmt) -> Result<bool> {
let sym = match pattern.pattern_symbol() {
Some(x) => x,
None => return Ok(false),
};
match self.types.get(&sym) {
Some(&bindings::Type::Stmt) => {}
Some(&bindings::Type::Unknown) => {}
None if sym.as_str().starts_with("__") => {}
_ => return Ok(false),
}
let ok = self.bindings.try_add(sym, target.clone());
if ok {
Ok(true)
} else {
Err(Error::NonlinearMismatch)
}
}
pub fn do_marked<T, F>(&mut self, tts: &TokenStream, func: F, target: &T) -> Result<()>
where
T: TryMatch + GetNodeId,
F: for<'b> FnOnce(&mut Parser<'b>) -> PResult<'b, T>,
{
let mut p = Parser::new(
&self.cx.session().parse_sess,
tts.clone().into(),
None,
false,
false,
);
let pattern = func(&mut p).unwrap();
let label = if p.eat(&Token::Comma) {
p.parse_ident().unwrap().name
} else {
"target".into_symbol()
};
if !self.st.marked(target.get_node_id(), label) {
return Err(Error::NotMarked);
}
self.try_match(&pattern, target)
}
fn do_def_impl(
&mut self,
tts: &TokenStream,
style: PathStyle,
opt_def_id: Option<DefId>,
) -> Result<()> {
let mut p = Parser::new(
&self.cx.session().parse_sess,
tts.clone().into(),
None,
false,
false,
);
let path_pattern = p.parse_path(style).unwrap();
let def_id = match_or!([opt_def_id] Some(x) => x;
return Err(Error::DefMismatch));
let (_qself, def_path) = reflect::reflect_def_path(self.cx.ty_ctxt(), def_id);
if self.debug {
eprintln!(
"def!(): trying to match pattern {:?} against AST {:?}",
path_pattern, def_path
);
}
if self.try_match(&path_pattern, &def_path).is_err() {
return Err(Error::DefMismatch);
}
Ok(())
}
pub fn do_def_expr(&mut self, tts: &TokenStream, target: &Expr) -> Result<()> {
let opt_def_id = self.cx.try_resolve_expr(target);
self.do_def_impl(tts, PathStyle::Expr, opt_def_id)
}
pub fn do_def_ty(&mut self, tts: &TokenStream, target: &Ty) -> Result<()> {
let opt_def_id = self.cx.try_resolve_ty(target);
self.do_def_impl(tts, PathStyle::Type, opt_def_id)
}
pub fn do_typed<T, F>(&mut self, tts: &TokenStream, func: F, target: &T) -> Result<()>
where
T: TryMatch + GetNodeId,
F: for<'b> FnOnce(&mut Parser<'b>) -> PResult<'b, T>,
{
let mut p = Parser::new(
&self.cx.session().parse_sess,
tts.clone().into(),
None,
false,
false,
);
let pattern = func(&mut p).unwrap();
p.expect(&Token::Comma).unwrap();
let ty_pattern = p.parse_ty().unwrap();
let tcx_ty = self
.cx
.opt_node_type(target.get_node_id())
.ok_or(Error::TypeUnavailable)?;
let ast_ty = reflect::reflect_tcx_ty(self.cx.ty_ctxt(), tcx_ty);
if self.debug {
eprintln!(
"typed!(): trying to match pattern {:?} against AST {:?}",
ty_pattern, ast_ty
);
}
if self.try_match(&ty_pattern, &ast_ty).is_err() {
return Err(Error::WrongType);
}
self.try_match(&pattern, target)
}
pub fn do_cast<F>(&mut self, tts: &TokenStream, func: F, target: &Expr) -> Result<()>
where
F: for<'b> FnOnce(&mut Parser<'b>) -> PResult<'b, P<Expr>>,
{
let ts: TokenStream = tts.clone().into();
let pattern = driver::run_parser_tts(self.cx.session(), ts.into_trees().collect(), func);
let mut target = target;
loop {
let old_bnd = self.bindings.clone();
let err = match self.try_match::<Expr>(&pattern, target) {
Ok(()) => return Ok(()),
Err(err) => err,
};
self.bindings = old_bnd;
target = match target.node {
ExprKind::Cast(ref e, _) => e,
_ => return Err(err),
};
}
}
}
fn make_bindings_parser<'a>(sess: &'a Session, src: &str) -> (Parser<'a>, BindingTypes) {
let ts = parse::parse_stream_from_source_str(
FileName::anon_source_code(src),
src.to_owned(),
&sess.parse_sess,
None,
);
let (ts, bt) = parse_bindings(ts);
(parse::stream_to_parser(&sess.parse_sess, ts), bt)
}
pub trait TryMatch {
fn try_match(&self, target: &Self, mcx: &mut MatchCtxt) -> Result<()>;
}
pub trait Pattern<V>: TryMatch + Sized {
fn visit<'a, 'tcx, T, F>(self, _init_mcx: MatchCtxt<'a, 'tcx>, _callback: F, _target: &mut T)
where
T: MutVisit,
F: FnMut(&mut V, MatchCtxt<'a, 'tcx>),
{
}
fn flat_map<'a, 'tcx, T, F>(self, _init_mcx: MatchCtxt<'a, 'tcx>, _callback: F, _target: &mut T)
where
T: MutVisit,
F: FnMut(V, MatchCtxt<'a, 'tcx>) -> SmallVec<[V; 1]>,
{
}
}
macro_rules! gen_pattern_impl {
(
pattern = $Pat:ty;
folder = $PatternFolder:ident;
fn $fold_thing:ident ( &mut $slf:ident , $arg:ident : $ArgTy:ty ) -> $RetTy:ty;
walk = $walk:expr;
map($match_one:ident) = $map:expr;
) => {
pub struct $PatternFolder<'a, 'tcx: 'a, F>
where F: FnMut(&mut $Pat, MatchCtxt<'a, 'tcx>) {
pattern: $Pat,
init_mcx: MatchCtxt<'a, 'tcx>,
callback: F,
}
impl<'a, 'tcx, F> MutVisitor for $PatternFolder<'a, 'tcx, F>
where F: FnMut(&mut $Pat, MatchCtxt<'a, 'tcx>) {
#[allow(unused_mut)]
fn $fold_thing(&mut $slf, $arg: $ArgTy) -> $RetTy {
let $arg = $walk;
let mut $match_one = |x: &mut $ArgTy| {
if let Ok(mcx) = $slf.init_mcx.clone_match(&$slf.pattern, &x) {
($slf.callback)(x, mcx)
}
};
$map
}
}
impl Pattern<$Pat> for $Pat {
fn visit<'a, 'tcx, T, F>(
self,
init_mcx: MatchCtxt<'a, 'tcx>,
callback: F,
target: &mut T,
)
where T: MutVisit,
F: FnMut(&mut Self, MatchCtxt<'a, 'tcx>)
{
let mut f = $PatternFolder {
pattern: self,
init_mcx: init_mcx,
callback: callback,
};
target.visit(&mut f)
}
}
};
(
pattern = $Pat:ty;
folder = $PatternFolder:ident;
fn $fold_thing:ident ( &mut $slf:ident , $arg:ident : &mut $ArgTy:ty );
walk = $walk:expr;
map($match_one:ident) = $map:expr;
) => {
pub struct $PatternFolder<'a, 'tcx: 'a, F>
where F: FnMut(&mut $Pat, MatchCtxt<'a, 'tcx>) {
pattern: $Pat,
init_mcx: MatchCtxt<'a, 'tcx>,
callback: F,
}
impl<'a, 'tcx, F> MutVisitor for $PatternFolder<'a, 'tcx, F>
where F: FnMut(&mut $Pat, MatchCtxt<'a, 'tcx>)
{
#[allow(unused_mut)]
fn $fold_thing(&mut $slf, $arg: &mut $ArgTy) {
$walk;
let mut $match_one = |x: &mut $ArgTy| {
if let Ok(mcx) = $slf.init_mcx.clone_match(&$slf.pattern, &x) {
($slf.callback)(x, mcx);
}
};
$map
}
}
impl Pattern<$Pat> for $Pat {
fn visit<'a, 'tcx, T, F>(
self,
init_mcx: MatchCtxt<'a, 'tcx>,
callback: F,
target: &mut T,
)
where T: MutVisit,
F: FnMut(&mut Self, MatchCtxt<'a, 'tcx>)
{
let mut f = $PatternFolder {
pattern: self,
init_mcx: init_mcx,
callback: callback,
};
target.visit(&mut f)
}
}
};
}
gen_pattern_impl! {
pattern = P<Expr>;
folder = ExprPatternFolder;
fn visit_expr(&mut self, e: &mut P<Expr>);
walk = mut_visit::noop_visit_expr(e, self);
map(match_one) = match_one(e);
}
gen_pattern_impl! {
pattern = P<Ty>;
folder = TyPatternFolder;
fn visit_ty(&mut self, t: &mut P<Ty>);
walk = mut_visit::noop_visit_ty(t, self);
map(match_one) = match_one(t);
}
gen_pattern_impl! {
pattern = Stmt;
folder = StmtPatternFolder;
fn flat_map_stmt(&mut self, s: Stmt) -> SmallVec<[Stmt; 1]>;
walk = mut_visit::noop_flat_map_stmt(s, self);
map(match_one) = { let mut s = s; s.iter_mut().for_each(match_one); s };
}
pub struct MultiStmtPatternFolder<'a, 'tcx: 'a, F>
where
F: FnMut(&mut Vec<Stmt>, MatchCtxt<'a, 'tcx>),
{
pattern: Vec<Stmt>,
init_mcx: MatchCtxt<'a, 'tcx>,
callback: F,
}
impl<'a, 'tcx, F> MutVisitor for MultiStmtPatternFolder<'a, 'tcx, F>
where
F: FnMut(&mut Vec<Stmt>, MatchCtxt<'a, 'tcx>),
{
fn visit_block(&mut self, b: &mut P<Block>) {
assert!(self.pattern.len() > 0);
mut_visit::noop_visit_block(b, self);
let mut new_stmts = Vec::with_capacity(b.stmts.len());
let mut last = 0;
let mut i = 0;
while i < b.stmts.len() {
let mut mcx = self.init_mcx.clone();
let result = match_multi_stmt(&mut mcx, &self.pattern, &b.stmts[i..]);
if let Some(consumed) = result {
new_stmts.extend_from_slice(&b.stmts[last..i]);
let mut consumed_stmts = b.stmts[i..i + consumed].to_owned();
(self.callback)(&mut consumed_stmts, mcx);
new_stmts.extend(consumed_stmts);
i += cmp::max(consumed, 1);
last = i;
} else {
if self.pattern.len() > 0 && is_multi_stmt_glob(&self.init_mcx, &self.pattern[0]) {
break;
} else {
i += 1;
}
}
}
if last != 0 {
new_stmts.extend_from_slice(&b.stmts[last..]);
b.stmts = new_stmts;
}
}
}
pub fn match_multi_stmt(mcx: &mut MatchCtxt, pattern: &[Stmt], target: &[Stmt]) -> Option<usize> {
if pattern.len() == 0 {
return Some(0);
}
if is_multi_stmt_glob(mcx, &pattern[0]) {
let name = pattern[0].pattern_symbol().unwrap();
for i in (0..target.len() + 1).rev() {
let orig_mcx = mcx.clone();
if let Some(consumed) = match_multi_stmt(mcx, &pattern[1..], &target[i..]) {
let ok = mcx.bindings.try_add(name, target[..i].to_owned());
if ok {
return Some(i + consumed);
}
}
*mcx = orig_mcx;
}
None
} else {
let mut i = 0;
while i < pattern.len() {
if is_multi_stmt_glob(mcx, &pattern[i]) {
match match_multi_stmt(mcx, &pattern[i..], &target[i..]) {
Some(consumed) => return Some(i + consumed),
None => return None,
}
}
if i >= target.len() {
return None;
}
let r = mcx.try_match(&pattern[i], &target[i]);
match r {
Ok(_) => {}
Err(_) => return None,
}
i += 1;
}
assert!(i == pattern.len());
Some(pattern.len())
}
}
fn is_multi_stmt_glob(mcx: &MatchCtxt, pattern: &Stmt) -> bool {
let sym = match pattern.pattern_symbol() {
Some(x) => x,
None => return false,
};
match mcx.types.get(&sym) {
Some(&bindings::Type::MultiStmt) => {}
None if sym.as_str().starts_with("__m_") => {}
_ => return false,
}
true
}
impl Pattern<Vec<Stmt>> for Vec<Stmt> {
fn visit<'a, 'tcx, T, F>(self, init_mcx: MatchCtxt<'a, 'tcx>, callback: F, target: &mut T)
where
T: MutVisit,
F: FnMut(&mut Vec<Stmt>, MatchCtxt<'a, 'tcx>),
{
let mut f = MultiStmtPatternFolder {
pattern: self,
init_mcx: init_mcx,
callback: callback,
};
target.visit(&mut f)
}
}
pub fn mut_visit_match<P, T, F>(
st: &CommandState,
cx: &RefactorCtxt,
pattern: P,
target: &mut T,
callback: F,
) where
P: Pattern<P>,
T: MutVisit,
F: FnMut(&mut P, MatchCtxt),
{
mut_visit_match_with(MatchCtxt::new(st, cx), pattern, target, callback)
}
pub fn mut_visit_match_with<'a, 'tcx, P, T, V, F>(
init_mcx: MatchCtxt<'a, 'tcx>,
pattern: P,
target: &mut T,
callback: F,
) where
P: Pattern<V>,
T: MutVisit,
F: FnMut(&mut V, MatchCtxt<'a, 'tcx>),
{
pattern.visit(init_mcx, callback, target)
}
pub fn flat_map_match_with<'a, 'tcx, P, T, V, F>(
init_mcx: MatchCtxt<'a, 'tcx>,
pattern: P,
target: &mut T,
callback: F,
) where
P: Pattern<V>,
T: MutVisit,
F: FnMut(V, MatchCtxt<'a, 'tcx>) -> SmallVec<[V; 1]>,
{
pattern.flat_map(init_mcx, callback, target)
}
pub fn find_first_with<P, T>(init_mcx: MatchCtxt, pattern: P, target: &mut T) -> Option<Bindings>
where
P: Pattern<P>,
T: MutVisit,
{
let mut result = None;
mut_visit_match_with(init_mcx, pattern, target, |_p, mcx| {
if result.is_none() {
result = Some(mcx.bindings);
}
});
result
}
pub fn find_first<P, T>(
st: &CommandState,
cx: &RefactorCtxt,
pattern: P,
target: &mut T,
) -> Option<Bindings>
where
P: Pattern<P>,
T: MutVisit,
{
find_first_with(MatchCtxt::new(st, cx), pattern, target)
}
pub fn replace_expr<T: MutVisit>(
st: &CommandState,
cx: &RefactorCtxt,
ast: &mut T,
pat: &str,
repl: &str,
) {
let mut mcx = MatchCtxt::new(st, cx);
let pat = mcx.parse_expr(pat);
let repl = mcx.parse_expr(repl);
mut_visit_match_with(mcx, pat, ast, |x, mcx| {
*x = repl.clone().subst(st, cx, &mcx.bindings)
})
}
pub fn replace_stmts<T: MutVisit>(
st: &CommandState,
cx: &RefactorCtxt,
ast: &mut T,
pat: &str,
repl: &str,
) {
let mut mcx = MatchCtxt::new(st, cx);
let pat = mcx.parse_stmts(pat);
let repl = mcx.parse_stmts(repl);
mut_visit_match_with(mcx, pat, ast, |x, mcx| {
*x = repl.clone().subst(st, cx, &mcx.bindings)
})
}