use super::hir_id_from_path;
use crate::code_substitution::CodeSubstitution;
use crate::definitions::RerastDefinitions;
use crate::rule_finder::StartMatch;
use crate::rules::{Rule, Rules};
use crate::Config;
use rustc::hir::{self, intravisit, HirId};
use rustc::infer::{self, InferCtxt};
use rustc::traits::ObligationCause;
use rustc::ty::subst::Subst;
use rustc::ty::{self, TyCtxt};
use std::collections::HashMap;
use std::fmt::Debug;
use std::mem;
use syntax;
use syntax::ast;
use syntax::ptr::P;
use syntax::source_map::{self, Spanned};
use syntax::symbol::Symbol;
use syntax_pos::{Span, SpanSnippetError, DUMMY_SP};
#[macro_export]
macro_rules! debug {
($state:expr, $($args:expr),*) => {
if $state.debug_active {
println!($($args),*);
}
}
}
pub(crate) struct RuleMatcher<'r, 'tcx: 'r> {
tcx: TyCtxt<'tcx>,
rules: &'r Rules<'tcx>,
matches: Matches<'r, 'tcx>,
rule_mod_symbol: Symbol,
parent_expr: Option<&'tcx hir::Expr>,
body_id: Option<hir::BodyId>,
rerast_definitions: RerastDefinitions<'tcx>,
config: Config,
debug_active: bool,
}
impl<'r, 'tcx> RuleMatcher<'r, 'tcx> {
pub(crate) fn find_matches(
tcx: TyCtxt<'tcx>,
rerast_definitions: RerastDefinitions<'tcx>,
krate: &'tcx hir::Crate,
rules: &'r Rules<'tcx>,
config: Config,
) -> Matches<'r, 'tcx> {
let mut matcher = RuleMatcher {
tcx,
rules,
matches: Matches::new(),
rule_mod_symbol: Symbol::intern(super::RULES_MOD_NAME),
parent_expr: None,
body_id: None,
rerast_definitions,
config,
debug_active: false,
};
intravisit::walk_crate(&mut matcher, krate);
matcher.matches
}
fn should_debug_node<T: StartMatch + 'tcx>(&self, node: &'tcx T) -> bool {
if self.config.debug_snippet.is_empty() {
return false;
}
self.tcx
.sess
.source_map()
.span_to_snippet(node.span())
.map(|snippet| {
if self.config.debug_snippet == "list_all" {
println!("snippet: {}", snippet);
}
snippet == self.config.debug_snippet
})
.unwrap_or(false)
}
fn get_first_match<T: StartMatch + 'tcx>(
&mut self,
node: &'tcx T,
parent_node: Option<&'tcx T>,
rules: &'r [Rule<'tcx, T>],
) -> Option<Match<'r, 'tcx, T>> {
self.debug_active = self.should_debug_node(node);
debug!(self, "node: {:?}", node);
for rule in rules {
if let Some((_, original_span)) = get_original_spans(rule.search.span(), node.span()) {
let m = self.get_match(node, parent_node, original_span, rule);
if m.is_some() {
debug!(self, "Matched");
self.debug_active = false;
return m;
}
}
}
debug!(self, "Didn't match");
self.debug_active = false;
None
}
fn get_match<T: StartMatch + 'tcx>(
&mut self,
node: &'tcx T,
parent_node: Option<&'tcx T>,
original_span: Span,
rule: &'r Rule<'tcx, T>,
) -> Option<Match<'r, 'tcx, T>> {
let rule_fn_id = self.tcx.hir().body_owner_def_id(rule.body_id);
let rule_tables = self.tcx.body_tables(rule.body_id);
let maybe_match_placeholders = self.tcx.infer_ctxt().enter(|infcx| {
let tcx = infcx.tcx;
let substs = infcx.fresh_substs_for_item(tcx.def_span(rule_fn_id), rule_fn_id);
let placeholder_types_by_id = rule
.placeholder_ids
.iter()
.map(|hir_id| {
(*hir_id, {
let ty = rule_tables.node_type(*hir_id);
ty.subst(tcx, substs)
})
})
.collect();
let mut match_state = MatchState {
tcx,
infcx,
body_id: self.body_id,
rule_type_tables: rule_tables,
match_placeholders: MatchPlaceholders::new(),
placeholder_types_by_id,
rerast_definitions: self.rerast_definitions,
bindings_can_match_patterns: T::bindings_can_match_patterns(),
debug_active: self.debug_active,
};
if rule.search.attempt_match(&mut match_state, node) {
Some(match_state.match_placeholders)
} else {
None
}
});
maybe_match_placeholders.map(|match_placeholders| Match {
rule,
node,
match_placeholders,
parent_node,
original_span,
})
}
fn process_children_of_expression(&mut self, expr: &'tcx hir::Expr) {
let old_parent = self.parent_expr;
self.parent_expr = Some(expr);
intravisit::walk_expr(self, expr);
self.parent_expr = old_parent;
}
fn find_matches_within_placeholders<T: StartMatch + 'tcx>(
&mut self,
m: &mut Match<'r, 'tcx, T>,
) {
for placeholder in m.match_placeholders.placeholders_by_id.values_mut() {
mem::swap(&mut placeholder.matches, &mut self.matches);
match placeholder.contents {
PlaceholderContents::Expr(placeholder_expr) => {
if placeholder_expr.hir_id == StartMatch::hir_id(m.node) {
self.process_children_of_expression(placeholder_expr);
} else {
intravisit::Visitor::visit_expr(self, placeholder_expr);
}
}
PlaceholderContents::Statements(placeholder_stmts) => {
for stmt in placeholder_stmts {
intravisit::Visitor::visit_stmt(self, stmt);
}
}
PlaceholderContents::Pattern(pattern) => {
intravisit::Visitor::visit_pat(self, pattern);
}
}
mem::swap(&mut placeholder.matches, &mut self.matches);
}
}
}
impl<'r, 'tcx> intravisit::Visitor<'tcx> for RuleMatcher<'r, 'tcx> {
fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
intravisit::NestedVisitorMap::All(&self.tcx.hir())
}
fn visit_item(&mut self, item: &'tcx hir::Item) {
if let hir::ItemKind::Mod(_) = item.node {
if item.ident.name == self.rule_mod_symbol {
return;
}
}
intravisit::walk_item(self, item);
}
fn visit_trait_ref(&mut self, trait_ref: &'tcx hir::TraitRef) {
if let Some(m) = self.get_first_match(trait_ref, None, &self.rules.trait_ref_rules) {
self.matches.trait_ref_matches.push(m);
} else {
intravisit::walk_trait_ref(self, trait_ref);
}
}
fn visit_body(&mut self, body: &'tcx hir::Body) {
let old_body_id = self.body_id;
self.body_id = Some(body.id());
match body.value.node {
hir::ExprKind::Block(_, _) => {
for arg in &body.arguments {
self.visit_id(arg.hir_id);
self.visit_pat(&arg.pat);
}
self.process_children_of_expression(&body.value);
}
_ => {
intravisit::walk_body(self, body);
}
}
self.body_id = old_body_id;
}
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
let parent_expr = self.parent_expr;
if let Some(mut m) = self.get_first_match(expr, parent_expr, &self.rules.expr_rules) {
self.find_matches_within_placeholders(&mut m);
self.matches.expr_matches.push(m);
} else {
self.process_children_of_expression(expr);
}
}
fn visit_ty(&mut self, ty: &'tcx hir::Ty) {
if let Some(m) = self.get_first_match(ty, None, &self.rules.type_rules) {
self.matches.type_matches.push(m);
return;
}
intravisit::walk_ty(self, ty);
}
fn visit_pat(&mut self, pat: &'tcx hir::Pat) {
if let Some(mut m) = self.get_first_match(pat, None, &self.rules.pattern_rules) {
self.find_matches_within_placeholders(&mut m);
self.matches.pattern_matches.push(m);
} else {
intravisit::walk_pat(self, pat);
}
}
}
pub(crate) struct MatchState<'r, 'a, 'tcx: 'a> {
tcx: TyCtxt<'tcx>,
infcx: InferCtxt<'a, 'tcx>,
body_id: Option<hir::BodyId>,
rule_type_tables: &'tcx ty::TypeckTables<'tcx>,
match_placeholders: MatchPlaceholders<'r, 'tcx>,
placeholder_types_by_id: HashMap<HirId, ty::Ty<'tcx>>,
rerast_definitions: RerastDefinitions<'tcx>,
bindings_can_match_patterns: bool,
debug_active: bool,
}
impl<'r, 'a, 'tcx: 'a> MatchState<'r, 'a, 'tcx> {
fn attempt_to_bind_expr(&mut self, qpath: &hir::QPath, expr: &'tcx hir::Expr) -> bool {
if let Some(hir_id) = hir_id_from_path(qpath) {
if let Some(&p_ty) = self.placeholder_types_by_id.get(&hir_id) {
let c_ty = self.code_type_tables().expr_ty(expr);
let c_ty_adjusted = self.code_type_tables().expr_ty_adjusted(expr);
let cause = ObligationCause::dummy();
let param_env = ty::ParamEnv::empty();
if self.infcx.at(&cause, param_env).sub(p_ty, c_ty).is_err()
&& self
.infcx
.at(&cause, param_env)
.sub(p_ty, c_ty_adjusted)
.is_err()
{
debug!(self, "Types didn't match");
return false;
}
let old_placeholder = self
.match_placeholders
.placeholders_by_id
.insert(hir_id, Placeholder::new(PlaceholderContents::Expr(expr)));
assert!(old_placeholder.is_none());
return true;
}
}
false
}
fn can_sub<T>(&self, a: T, b: T) -> bool
where
T: infer::at::ToTrace<'tcx>,
{
self.infcx.can_sub(ty::ParamEnv::empty(), a, b).is_ok()
}
fn fn_expr_equals_method_call(
&self,
fn_expr: &hir::Expr,
fn_type_tables: &ty::TypeckTables<'tcx>,
method_call_id: HirId,
method_type_tables: &ty::TypeckTables<'tcx>,
) -> bool {
if let Some(Ok((hir::def::DefKind::Method, method_id))) =
fn_type_tables.type_dependent_defs().get(fn_expr.hir_id)
{
if let Ok((_, method_def_id)) = method_type_tables.type_dependent_defs()[method_call_id]
{
return method_def_id == *method_id;
}
}
false
}
fn opt_statements_placeholder_hir_id(&self, stmt: &hir::Stmt) -> Option<HirId> {
if let hir::StmtKind::Semi(ref expr) = stmt.node {
if let hir::ExprKind::Call(ref function, _) = expr.node {
let fn_ty = self.rule_type_tables.expr_ty(function);
if !self.can_sub(fn_ty, self.rerast_definitions.statements) {
return None;
}
if let hir::ExprKind::Path(ref path) = function.node {
if let Some(hir_id) = hir_id_from_path(path) {
if self.placeholder_types_by_id.contains_key(&hir_id) {
return Some(hir_id);
}
}
}
}
}
None
}
fn code_type_tables(&self) -> &'tcx ty::TypeckTables<'tcx> {
self.tcx.body_tables(self.body_id.unwrap())
}
fn span_to_snippet(&self, span: Span) -> Result<String, SpanSnippetError> {
self.tcx.sess.source_map().span_to_snippet(span)
}
}
pub(crate) trait Matchable: Debug {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool;
}
impl<T: Matchable> Matchable for Option<T> {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
match (self.as_ref(), code.as_ref()) {
(None, None) => true,
(None, Some(_)) | (Some(_), None) => false,
(Some(p), Some(c)) => p.attempt_match(state, c),
}
}
}
impl<T: Matchable> Matchable for [T] {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.len() == code.len()
&& self
.iter()
.zip(code.iter())
.all(|(p, c)| p.attempt_match(state, c))
}
}
impl<T: Matchable> Matchable for P<T> {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
(**self).attempt_match(state, &**code)
}
}
impl<T: Matchable> Matchable for Spanned<T> {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.node.attempt_match(state, &code.node)
}
}
impl Matchable for hir::Expr {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
use rustc::hir::ExprKind;
let result = match (&self.node, &code.node) {
(&ExprKind::Call(ref p_fn, ref p_args), &ExprKind::Call(ref c_fn, ref c_args)) => {
p_fn.attempt_match(state, c_fn) && p_args.attempt_match(state, c_args)
}
(
&ExprKind::MethodCall(ref p_name, _, ref p_args),
&ExprKind::MethodCall(ref c_name, _, ref c_args),
) => p_name.attempt_match(state, c_name) && p_args.attempt_match(state, c_args),
(&ExprKind::MethodCall(_, _, ref p_args), &ExprKind::Call(ref c_fn, ref c_args)) => {
state.fn_expr_equals_method_call(
c_fn,
state.code_type_tables(),
self.hir_id,
state.rule_type_tables,
) && p_args.attempt_match(state, c_args)
}
(&ExprKind::Call(ref p_fn, ref p_args), &ExprKind::MethodCall(_, _, ref c_args)) => {
state.fn_expr_equals_method_call(
p_fn,
state.rule_type_tables,
code.hir_id,
state.code_type_tables(),
) && p_args.attempt_match(state, c_args)
}
(
&ExprKind::Binary(ref p_op, ref p1, ref p2),
&ExprKind::Binary(ref c_op, ref c1, ref c2),
) => {
p_op.attempt_match(state, c_op)
&& p1.attempt_match(state, c1)
&& p2.attempt_match(state, c2)
}
(&ExprKind::Unary(p_op, ref p_expr), &ExprKind::Unary(c_op, ref c_expr)) => {
p_op == c_op && p_expr.attempt_match(state, c_expr)
}
(&ExprKind::AddrOf(p_mut, ref p_expr), &ExprKind::AddrOf(c_mut, ref c_expr)) => {
p_mut == c_mut && p_expr.attempt_match(state, c_expr)
}
(&ExprKind::Lit(ref p_lit), &ExprKind::Lit(ref c_lit)) => {
p_lit.node == c_lit.node || {
if let Some((p_span, c_span)) = get_original_spans(self.span, code.span) {
state.span_to_snippet(p_span) == state.span_to_snippet(c_span)
} else {
false
}
}
}
(
&ExprKind::Loop(ref p_block, ref p_name, ref p_type),
&ExprKind::Loop(ref c_block, ref c_name, ref c_type),
) => {
p_type == c_type
&& p_name.attempt_match(state, c_name)
&& p_block.attempt_match(state, c_block)
}
(&ExprKind::Tup(ref p_vec), &ExprKind::Tup(ref c_vec))
| (&ExprKind::Array(ref p_vec), &ExprKind::Array(ref c_vec)) => {
p_vec.attempt_match(state, c_vec)
}
(
&ExprKind::Repeat(ref p_expr, ref p_const),
&ExprKind::Repeat(ref c_expr, ref c_const),
) => p_expr.attempt_match(state, c_expr) && p_const.attempt_match(state, c_const),
(
&ExprKind::Match(ref p_expr, ref p_arms, ref p_source),
&ExprKind::Match(ref c_expr, ref c_arms, ref c_source),
) => {
p_expr.attempt_match(state, c_expr)
&& p_source == c_source
&& p_arms.attempt_match(state, c_arms)
}
(
&ExprKind::Struct(ref p_path, ref p_fields, ref p_expr),
&ExprKind::Struct(ref c_path, ref c_fields, ref c_expr),
) => {
p_path.attempt_match(state, c_path)
&& p_fields.attempt_match(state, c_fields)
&& p_expr.attempt_match(state, c_expr)
}
(
&ExprKind::Block(ref p_block, ref p_label),
&ExprKind::Block(ref c_block, ref c_label),
) => p_block.attempt_match(state, c_block) && p_label.attempt_match(state, c_label),
(&ExprKind::Cast(ref p_expr, ref _p_ty), &ExprKind::Cast(ref c_expr, ref _c_ty)) => {
p_expr.attempt_match(state, c_expr)
&& state.can_sub(
state.rule_type_tables.expr_ty(self),
state.code_type_tables().expr_ty(code),
)
}
(
&ExprKind::Index(ref p_expr, ref p_index),
&ExprKind::Index(ref c_expr, ref c_index),
) => p_expr.attempt_match(state, c_expr) && p_index.attempt_match(state, c_index),
(
&ExprKind::Field(ref p_expr, ref p_name),
&ExprKind::Field(ref c_expr, ref c_name),
) => p_expr.attempt_match(state, c_expr) && p_name.attempt_match(state, c_name),
(&ExprKind::Assign(ref p_lhs, ref p_rhs), &ExprKind::Assign(ref c_lhs, ref c_rhs)) => {
p_lhs.attempt_match(state, c_lhs) && p_rhs.attempt_match(state, c_rhs)
}
(
&ExprKind::AssignOp(ref p_op, ref p_lhs, ref p_rhs),
&ExprKind::AssignOp(ref c_op, ref c_lhs, ref c_rhs),
) => {
p_op.attempt_match(state, c_op)
&& p_lhs.attempt_match(state, c_lhs)
&& p_rhs.attempt_match(state, c_rhs)
}
(
&ExprKind::Break(ref p_label, ref p_expr),
&ExprKind::Break(ref c_label, ref c_expr),
) => p_label.attempt_match(state, c_label) && p_expr.attempt_match(state, c_expr),
(&ExprKind::Continue(ref p_label), &ExprKind::Continue(ref c_label)) => {
p_label.attempt_match(state, c_label)
}
(
&ExprKind::While(ref p_expr, ref p_block, ref p_name),
&ExprKind::While(ref c_expr, ref c_block, ref c_name),
) => {
p_expr.attempt_match(state, c_expr)
&& p_block.attempt_match(state, c_block)
&& p_name.attempt_match(state, c_name)
}
(
&ExprKind::Closure(ref p_capture, _, ref p_body_id, _, ref p_gen),
&ExprKind::Closure(ref c_capture, _, ref c_body_id, _, ref c_gen),
) => {
p_capture.attempt_match(state, c_capture)
&& p_body_id.attempt_match(state, c_body_id)
&& p_gen == c_gen
}
(&ExprKind::Ret(ref p_expr), &ExprKind::Ret(ref c_expr)) => {
p_expr.attempt_match(state, c_expr)
}
(&ExprKind::Box(ref p_expr), &ExprKind::Box(ref c_expr)) => {
p_expr.attempt_match(state, c_expr)
}
(&ExprKind::DropTemps(ref p_expr), &ExprKind::DropTemps(ref c_expr)) => {
p_expr.attempt_match(state, c_expr)
}
(&ExprKind::Yield(ref p_expr, _), &ExprKind::Yield(ref c_expr, _)) => {
p_expr.attempt_match(state, c_expr)
}
(&ExprKind::Path(ref p_path), &ExprKind::Path(ref c_path)) => {
if state.attempt_to_bind_expr(p_path, code) {
return true;
}
p_path.attempt_match(state, c_path)
}
(&ExprKind::Path(ref path), _) => {
if state.attempt_to_bind_expr(path, code) {
return true;
}
false
}
_ => false,
};
if !result {
debug!(
state,
"Expression: {:?}\ndidn't match: {:?}", code.node, self.node
);
}
result && all_expansions_equal(self.span, code.span())
}
}
impl Matchable for hir::Body {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.arguments.attempt_match(state, &code.arguments)
&& self.value.attempt_match(state, &code.value)
}
}
impl Matchable for hir::Arg {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.pat.attempt_match(state, &code.pat)
}
}
impl Matchable for hir::Ty {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.node.attempt_match(state, &code.node)
}
}
impl Matchable for hir::TyKind {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
use crate::hir::TyKind;
match (self, code) {
(&TyKind::Slice(ref p), &TyKind::Slice(ref c))
| (&TyKind::Array(ref p, _), &TyKind::Array(ref c, _)) => p.attempt_match(state, c),
(&TyKind::Ptr(ref p), &TyKind::Ptr(ref c)) => p.attempt_match(state, c),
(&TyKind::Rptr(ref p_lifetime, ref p_ty), &TyKind::Rptr(ref c_lifetime, ref c_ty)) => {
p_lifetime.attempt_match(state, c_lifetime) && p_ty.attempt_match(state, c_ty)
}
(&TyKind::BareFn(ref p), &TyKind::BareFn(ref c)) => p.attempt_match(state, c),
(&TyKind::Never, &TyKind::Never) => true,
(&TyKind::Tup(ref p), &TyKind::Tup(ref c)) => p.attempt_match(state, c),
(&TyKind::Path(ref p), &TyKind::Path(ref c)) => p.attempt_match(state, c),
(
&TyKind::TraitObject(ref p, ref p_lifetime),
&TyKind::TraitObject(ref c, ref c_lifetime),
) => p.attempt_match(state, c) && p_lifetime.attempt_match(state, c_lifetime),
_ => false,
}
}
}
impl Matchable for hir::MutTy {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.mutbl == code.mutbl && self.ty.attempt_match(state, &code.ty)
}
}
impl Matchable for hir::Lifetime {
fn attempt_match<'r, 'a, 'tcx>(
&self,
_state: &mut MatchState<'r, 'a, 'tcx>,
_code: &'tcx Self,
) -> bool {
false
}
}
impl Matchable for hir::BareFnTy {
fn attempt_match<'r, 'a, 'tcx>(
&self,
_state: &mut MatchState<'r, 'a, 'tcx>,
_code: &'tcx Self,
) -> bool {
false
}
}
impl Matchable for hir::PolyTraitRef {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.trait_ref.attempt_match(state, &code.trait_ref)
}
}
impl Matchable for hir::CaptureClause {
fn attempt_match<'r, 'a, 'tcx>(
&self,
_state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
match (self, code) {
(hir::CaptureClause::CaptureByValue, hir::CaptureClause::CaptureByValue)
| (hir::CaptureClause::CaptureByRef, hir::CaptureClause::CaptureByRef) => true,
_ => false,
}
}
}
impl Matchable for ast::CrateSugar {
fn attempt_match<'r, 'a, 'tcx>(
&self,
_state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
match (self, code) {
(ast::CrateSugar::PubCrate, ast::CrateSugar::PubCrate)
| (ast::CrateSugar::JustCrate, ast::CrateSugar::JustCrate) => true,
_ => false,
}
}
}
impl Matchable for hir::TraitRef {
fn attempt_match<'r, 'a, 'tcx>(
&self,
_state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.path.res == code.path.res
}
}
impl Matchable for hir::GenericBound {
fn attempt_match<'r, 'a, 'tcx>(
&self,
_state: &mut MatchState<'r, 'a, 'tcx>,
_code: &'tcx Self,
) -> bool {
false
}
}
impl Matchable for hir::Arm {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.attrs.is_empty()
&& code.attrs.is_empty()
&& self.pats.attempt_match(state, &code.pats)
&& self.guard.attempt_match(state, &code.guard)
&& self.body.attempt_match(state, &code.body)
}
}
impl Matchable for hir::Guard {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
match (self, code) {
(hir::Guard::If(ref p_expr), hir::Guard::If(ref c_expr)) => {
p_expr.attempt_match(state, c_expr)
}
}
}
}
impl Matchable for hir::Pat {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
use crate::hir::PatKind::*;
match (&self.node, &code.node) {
(&Wild, &Wild) => true,
(&Binding(p_mode, p_hir_id, ref _p_name, ref p_pat), _) => {
if state.bindings_can_match_patterns {
state.match_placeholders.placeholders_by_id.insert(
p_hir_id,
Placeholder::new(PlaceholderContents::Pattern(code)),
);
true
} else if let Binding(c_mode, c_hir_id, ref c_name, ref c_pat) = code.node {
if p_mode == c_mode && p_pat.is_none() && c_pat.is_none() {
state.match_placeholders.matched_variable_decls.insert(
p_hir_id,
MatchedVariableDecl {
code_hir_id: c_hir_id,
name: c_name.to_string(),
},
);
true
} else {
false
}
} else {
false
}
}
(
&Struct(ref p_qpath, ref p_pats, p_dotdot),
&Struct(ref c_qpath, ref c_pats, c_dotdot),
) => {
fn sorted_by_name(
pats: &hir::HirVec<Spanned<hir::FieldPat>>,
) -> Vec<&Spanned<hir::FieldPat>> {
let mut result: Vec<_> = pats.iter().collect();
result.sort_by_key(|pat| pat.node.ident.name);
result
}
p_qpath.attempt_match(state, c_qpath)
&& p_dotdot == c_dotdot
&& p_pats.len() == c_pats.len()
&& sorted_by_name(p_pats)
.iter()
.zip(sorted_by_name(c_pats))
.all(|(p, c)| p.attempt_match(state, c))
}
(
&TupleStruct(ref p_qpath, ref p_pats, ref p_dd_pos),
&TupleStruct(ref c_qpath, ref c_pats, ref c_dd_pos),
) => {
p_qpath.attempt_match(state, c_qpath)
&& p_pats.attempt_match(state, c_pats)
&& p_dd_pos.attempt_match(state, c_dd_pos)
}
(&Box(ref p_pat), &Box(ref c_pat)) => p_pat.attempt_match(state, c_pat),
(&Tuple(ref p_patterns, ref p_dd_pos), &Tuple(ref c_patterns, ref c_dd_pos)) => {
p_patterns.attempt_match(state, c_patterns) && p_dd_pos == c_dd_pos
}
(&Ref(ref p_pat, ref p_mut), &Ref(ref c_pat, ref c_mut)) => {
p_pat.attempt_match(state, c_pat) && p_mut == c_mut
}
(
&Slice(ref p_pats_a, ref p_op_pat, ref p_pats_b),
&Slice(ref c_pats_a, ref c_op_pat, ref c_pats_b),
) => {
p_pats_a.attempt_match(state, c_pats_a)
&& p_op_pat.attempt_match(state, c_op_pat)
&& p_pats_b.attempt_match(state, c_pats_b)
}
(&Lit(ref p_expr), &Lit(ref c_expr)) => p_expr.attempt_match(state, c_expr),
(
&Range(ref p_ex1, ref p_ex2, ref p_incl),
&Range(ref c_ex1, ref c_ex2, ref c_incl),
) => {
p_ex1.attempt_match(state, c_ex1)
&& p_ex2.attempt_match(state, c_ex2)
&& p_incl == c_incl
}
(&Path(ref p), &Path(ref c)) => p.attempt_match(state, c),
_ => false,
}
}
}
impl Matchable for hir::Field {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.ident.attempt_match(state, &code.ident) && self.expr.attempt_match(state, &code.expr)
}
}
impl Matchable for hir::FieldPat {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.ident.attempt_match(state, &code.ident) && self.pat.attempt_match(state, &code.pat)
}
}
impl Matchable for hir::BinOp {
fn attempt_match<'r, 'a, 'tcx>(
&self,
_state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.node == code.node
}
}
impl Matchable for Symbol {
fn attempt_match<'r, 'a, 'tcx>(
&self,
_state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self == code
}
}
impl Matchable for usize {
fn attempt_match<'r, 'a, 'tcx>(
&self,
_state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self == code
}
}
impl Matchable for hir::Destination {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.label.attempt_match(state, &code.label)
}
}
impl Matchable for syntax::ast::Label {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.ident.name.attempt_match(state, &code.ident.name)
}
}
impl Matchable for ast::Ident {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.name.attempt_match(state, &code.name)
}
}
impl Matchable for hir::QPath {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
use crate::hir::QPath::*;
match (self, code) {
(&Resolved(ref p_ty, ref p_path), &Resolved(ref c_ty, ref c_path)) => {
p_ty.attempt_match(state, c_ty) && p_path.attempt_match(state, c_path)
}
(&TypeRelative(ref p_ty, ref p_segment), &TypeRelative(ref c_ty, ref c_segment)) => {
p_ty.attempt_match(state, c_ty) && p_segment.attempt_match(state, c_segment)
}
_ => false,
}
}
}
impl Matchable for hir::Path {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
match (self.res, code.res) {
(hir::def::Res::Local(p_hir_id), hir::def::Res::Local(c_hir_id)) => state
.match_placeholders
.matched_variable_decls
.get(&p_hir_id)
.map(|matched_var_decl| matched_var_decl.code_hir_id == c_hir_id)
.unwrap_or(false),
_ => self.res == code.res,
}
}
}
impl Matchable for hir::PathSegment {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.ident.name == code.ident.name && self.args.attempt_match(state, &code.args)
}
}
impl Matchable for hir::Stmt {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
use rustc::hir::StmtKind;
match (&self.node, &code.node) {
(&StmtKind::Expr(ref p), &StmtKind::Expr(ref c))
| (&StmtKind::Semi(ref p), &StmtKind::Semi(ref c)) => p.attempt_match(state, c),
(&StmtKind::Local(ref p), &StmtKind::Local(ref c)) => p.attempt_match(state, c),
(&StmtKind::Item(ref p), &StmtKind::Item(ref c)) => {
let krate = state.tcx.hir().krate();
krate.item(p.id).attempt_match(state, krate.item(c.id))
}
_ => false,
}
}
}
impl Matchable for hir::Local {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.pat.attempt_match(state, &code.pat)
&& self.ty.attempt_match(state, &code.ty)
&& self.init.attempt_match(state, &code.init)
&& self.attrs.attempt_match(state, &code.attrs)
}
}
impl Matchable for hir::Item {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
state.match_placeholders.matched_variable_decls.insert(
self.hir_id,
MatchedVariableDecl {
code_hir_id: code.hir_id,
name: code.ident.to_string(),
},
);
self.attrs.attempt_match(state, &*code.attrs)
&& self.vis.attempt_match(state, &code.vis)
&& self.node.attempt_match(state, &code.node)
}
}
impl Matchable for hir::ItemKind {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
use crate::hir::ItemKind;
match (self, code) {
(
&ItemKind::Static(ref p_ty, p_mut, ref p_body_id),
&ItemKind::Static(ref c_ty, c_mut, ref c_body_id),
) => {
p_ty.attempt_match(state, c_ty)
&& p_mut == c_mut
&& p_body_id.attempt_match(state, c_body_id)
}
_ => false,
}
}
}
impl Matchable for hir::AnonConst {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.body.attempt_match(state, &code.body)
}
}
impl Matchable for hir::BodyId {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
let p_body = state.tcx.hir().body(*self);
let c_body = state.tcx.hir().body(*code);
p_body.attempt_match(state, c_body)
}
}
impl Matchable for hir::Visibility {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
use crate::hir::VisibilityKind::*;
match (&self.node, &code.node) {
(
Restricted {
path: ref p_path, ..
},
Restricted {
path: ref c_path, ..
},
) => p_path.attempt_match(state, c_path),
(Crate(ref p_sugar), Crate(ref c_sugar)) => p_sugar.attempt_match(state, c_sugar),
(Public, Public) | (Inherited, Inherited) => true,
_ => false,
}
}
}
impl Matchable for ast::Attribute {
fn attempt_match<'r, 'a, 'tcx>(
&self,
_state: &mut MatchState<'r, 'a, 'tcx>,
_code: &'tcx Self,
) -> bool {
false
}
}
impl Matchable for hir::Block {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
if !self.stmts.attempt_match(state, &code.stmts) {
return false;
}
if !self.expr.attempt_match(state, &code.expr) {
return false;
}
true
}
}
impl Matchable for Vec<hir::Stmt> {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
for (i, stmt) in self.iter().enumerate() {
if let Some(hir_id) = state.opt_statements_placeholder_hir_id(stmt) {
if code.len() < self.len() + 1 {
return false;
}
let p_after = &self[i + 1..];
let c_after = &code[code.len() - p_after.len()..];
if self[..i].attempt_match(state, &code[..i])
&& p_after.attempt_match(state, c_after)
&& state.placeholder_types_by_id.contains_key(&hir_id)
{
state.match_placeholders.placeholders_by_id.insert(
hir_id,
Placeholder::new(PlaceholderContents::Statements(
&code[i..code.len() - p_after.len()],
)),
);
return true;
}
return false;
}
}
self.attempt_match(state, &code)
}
}
impl Matchable for hir::GenericArgs {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.parenthesized == code.parenthesized
&& self.args.attempt_match(state, &*code.args)
&& self.bindings.attempt_match(state, &*code.bindings)
}
}
impl Matchable for hir::GenericArg {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
match (self, code) {
(
hir::GenericArg::Lifetime(ref p_lifetime),
hir::GenericArg::Lifetime(ref c_lifetime),
) => p_lifetime.attempt_match(state, c_lifetime),
(hir::GenericArg::Type(ref p_type), hir::GenericArg::Type(ref c_type)) => {
p_type.attempt_match(state, c_type)
}
_ => false,
}
}
}
impl Matchable for hir::TypeBinding {
fn attempt_match<'r, 'a, 'tcx>(
&self,
state: &mut MatchState<'r, 'a, 'tcx>,
code: &'tcx Self,
) -> bool {
self.ident.name.attempt_match(state, &code.ident.name)
&& self.ty().attempt_match(state, &code.ty())
}
}
#[derive(Debug)]
pub(crate) struct Matches<'r, 'tcx: 'r> {
expr_matches: Vec<Match<'r, 'tcx, hir::Expr>>,
pattern_matches: Vec<Match<'r, 'tcx, hir::Pat>>,
type_matches: Vec<Match<'r, 'tcx, hir::Ty>>,
trait_ref_matches: Vec<Match<'r, 'tcx, hir::TraitRef>>,
}
impl<'r, 'tcx> Matches<'r, 'tcx> {
fn new() -> Matches<'r, 'tcx> {
Matches {
expr_matches: Vec::new(),
pattern_matches: Vec::new(),
type_matches: Vec::new(),
trait_ref_matches: Vec::new(),
}
}
}
#[derive(Debug)]
struct Match<'r, 'tcx: 'r, T: StartMatch> {
rule: &'r Rule<'tcx, T>,
node: &'tcx T,
parent_node: Option<&'tcx T>,
match_placeholders: MatchPlaceholders<'r, 'tcx>,
original_span: Span,
}
impl<'r, 'a, 'tcx, T: StartMatch> Match<'r, 'tcx, T> {
fn get_replacement_source(&self, tcx: TyCtxt<'tcx>) -> String {
let replacement = self.rule.replace;
let mut replacement_visitor = ReplacementVisitor {
tcx,
result: vec![],
current_match: self,
parent_expr: None,
substitute_hir_ids: HashMap::new(),
};
let source_map = tcx.sess.source_map();
T::walk(&mut replacement_visitor, replacement);
let substitutions = replacement_visitor.result;
CodeSubstitution::apply_with_source_map(
&substitutions,
source_map,
replacement.span().source_callsite(),
)
}
}
#[derive(Debug)]
pub(crate) struct MatchedVariableDecl {
code_hir_id: HirId,
name: String,
}
#[derive(Debug)]
pub(crate) struct MatchPlaceholders<'r, 'tcx: 'r> {
placeholders_by_id: HashMap<HirId, Placeholder<'r, 'tcx>>,
matched_variable_decls: HashMap<HirId, MatchedVariableDecl>,
}
impl<'r, 'a, 'tcx> MatchPlaceholders<'r, 'tcx> {
fn new() -> MatchPlaceholders<'r, 'tcx> {
MatchPlaceholders {
placeholders_by_id: HashMap::new(),
matched_variable_decls: HashMap::new(),
}
}
fn get_placeholder<'this>(&'this self, hir_id: HirId) -> Option<&'this Placeholder<'r, 'tcx>> {
self.placeholders_by_id.get(&hir_id)
}
}
#[derive(Debug, Copy, Clone)]
enum PlaceholderContents<'tcx> {
Expr(&'tcx hir::Expr),
Statements(&'tcx [hir::Stmt]),
Pattern(&'tcx hir::Pat),
}
impl<'tcx> PlaceholderContents<'tcx> {
fn get_span(&self, target: Span) -> Span {
use self::PlaceholderContents::*;
match *self {
Expr(expr) => span_within_span(expr.span, target),
Statements(stmts) => {
if let Some(stmt) = stmts.get(0) {
let result = span_within_span(stmt.span, target);
let last_span = span_within_span(stmts[stmts.len() - 1].span, target);
result.with_hi(last_span.hi())
} else {
DUMMY_SP
}
}
Pattern(pattern) => pattern.span,
}
}
fn needs_parenthesis(&self, parent_expr: Option<&hir::Expr>) -> bool {
if let PlaceholderContents::Expr(expr) = *self {
OperatorPrecedence::needs_parenthesis(parent_expr, expr)
} else {
false
}
}
}
#[derive(Debug)]
struct Placeholder<'r, 'tcx: 'r> {
contents: PlaceholderContents<'tcx>,
matches: Matches<'r, 'tcx>,
}
impl<'r, 'tcx: 'r> Placeholder<'r, 'tcx> {
fn new(contents: PlaceholderContents<'tcx>) -> Placeholder<'r, 'tcx> {
Placeholder {
contents,
matches: Matches::new(),
}
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug)]
pub(crate) enum OperatorPrecedence {
Unary, MulDivMod, AddSub, BitShift, BitAnd, BitXor, BitOr, Comparison, LogicalAnd, LogicalOr, Assignment, }
impl OperatorPrecedence {
fn from_expr(expr: &hir::Expr) -> Option<OperatorPrecedence> {
use rustc::hir::ExprKind;
Some(match expr.node {
ExprKind::Unary(..) => OperatorPrecedence::Unary,
ExprKind::Binary(op, ..) => {
use rustc::hir::BinOpKind;
match op.node {
BinOpKind::Add | BinOpKind::Sub => OperatorPrecedence::AddSub,
BinOpKind::Mul | BinOpKind::Div | BinOpKind::Rem => {
OperatorPrecedence::MulDivMod
}
BinOpKind::And => OperatorPrecedence::LogicalAnd,
BinOpKind::Or => OperatorPrecedence::LogicalOr,
BinOpKind::BitXor => OperatorPrecedence::BitXor,
BinOpKind::BitAnd => OperatorPrecedence::BitAnd,
BinOpKind::BitOr => OperatorPrecedence::BitOr,
BinOpKind::Shl | BinOpKind::Shr => OperatorPrecedence::BitShift,
BinOpKind::Eq
| BinOpKind::Lt
| BinOpKind::Le
| BinOpKind::Ne
| BinOpKind::Ge
| BinOpKind::Gt => OperatorPrecedence::Comparison,
}
}
ExprKind::Assign(..) => OperatorPrecedence::Assignment,
_ => return None,
})
}
pub(crate) fn needs_parenthesis(
maybe_parent_expr: Option<&hir::Expr>,
child_expr: &hir::Expr,
) -> bool {
if let (Some(parent), Some(child)) = (
maybe_parent_expr.and_then(OperatorPrecedence::from_expr),
OperatorPrecedence::from_expr(child_expr),
) {
child >= parent
} else {
false
}
}
}
fn get_original_spans(search_span: Span, code_span: Span) -> Option<(Span, Span)> {
match (
search_span.ctxt().outer().expn_info(),
code_span.ctxt().outer().expn_info(),
) {
(Some(search_expn), Some(code_expn)) => {
if is_same_expansion(&search_expn, &code_expn) {
get_original_spans(search_expn.call_site, code_expn.call_site)
} else {
None
}
}
(None, None) => Some((search_span, code_span)),
_ => None,
}
}
fn is_same_expansion(a: &source_map::ExpnInfo, b: &source_map::ExpnInfo) -> bool {
use crate::source_map::ExpnFormat::*;
a.format == b.format
&& match a.format {
MacroBang(_) => a.def_site == b.def_site,
MacroAttribute(_) => unimplemented!(),
CompilerDesugaring(_) => true,
}
}
fn span_within_span(span: Span, target: Span) -> Span {
if target.contains(span) {
span
} else if let Some(expn_info) = span.ctxt().outer().expn_info() {
span_within_span(expn_info.call_site, target)
} else {
panic!("We never found a span within the target span");
}
}
fn all_expansions_equal(rule_span: Span, code_span: Span) -> bool {
get_original_spans(rule_span, code_span).is_some()
}
struct ReplacementVisitor<'r, 'tcx, T: StartMatch> {
tcx: TyCtxt<'tcx>,
result: Vec<CodeSubstitution<Span>>,
current_match: &'r Match<'r, 'tcx, T>,
parent_expr: Option<&'tcx hir::Expr>,
substitute_hir_ids: HashMap<HirId, HirId>,
}
impl<'r, 'tcx, T: StartMatch> ReplacementVisitor<'r, 'tcx, T> {
fn hir_id_snippet(&self, hir_id: HirId) -> String {
let source_map = self.tcx.sess.source_map();
source_map
.span_to_snippet(self.tcx.hir().span(hir_id))
.unwrap()
}
fn process_expr(&mut self, expr: &'tcx hir::Expr, placeholder_span: Span) -> bool {
if let hir::ExprKind::Path(ref path) = expr.node {
if let Some(hir_id) = hir_id_from_path(path) {
if let Some(placeholder) = self
.current_match
.match_placeholders
.get_placeholder(hir_id)
{
self.process_placeholder(placeholder, placeholder_span);
return true;
} else if let Some(code_hir_id) = self.substitute_hir_ids.get(&hir_id) {
let code = self.hir_id_snippet(*code_hir_id);
self.result.push(CodeSubstitution::new(expr.span, code));
return true;
}
}
}
false
}
fn process_placeholder(&mut self, placeholder: &Placeholder<'r, 'tcx>, placeholder_span: Span) {
let source_map = self.tcx.sess.source_map();
let span = placeholder
.contents
.get_span(self.current_match.original_span);
let substitutions = substitions_for_matches(self.tcx, &placeholder.matches);
let new_code = CodeSubstitution::apply_with_source_map(&substitutions, source_map, span);
self.result.push(
CodeSubstitution::new(placeholder_span, new_code)
.needs_parenthesis(placeholder.contents.needs_parenthesis(self.parent_expr)),
);
}
}
impl<'r, 'tcx, T: StartMatch> intravisit::Visitor<'tcx>
for ReplacementVisitor<'r, 'tcx, T>
{
fn nested_visit_map<'this>(&'this mut self) -> intravisit::NestedVisitorMap<'this, 'tcx> {
intravisit::NestedVisitorMap::All(&self.tcx.hir())
}
fn visit_expr(&mut self, expr: &'tcx hir::Expr) {
self.process_expr(expr, expr.span);
let old_parent = self.parent_expr;
self.parent_expr = Some(expr);
intravisit::walk_expr(self, expr);
self.parent_expr = old_parent;
}
fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt) {
if let hir::StmtKind::Semi(ref expr) = stmt.node {
if let hir::ExprKind::Call(ref expr_fn, _) = expr.node {
if self.process_expr(expr_fn, stmt.span) {
return;
}
}
}
intravisit::walk_stmt(self, stmt);
}
fn visit_pat(&mut self, pat: &'tcx hir::Pat) {
if let hir::PatKind::Binding(_, hir_id, ref ident, _) = pat.node {
if let Some(search_hir_id) = self
.current_match
.rule
.declared_name_hir_ids
.get(&ident.name)
{
if let Some(placeholder) = self
.current_match
.match_placeholders
.get_placeholder(*search_hir_id)
{
self.process_placeholder(placeholder, pat.span);
} else if let Some(matched_var_decl) = self
.current_match
.match_placeholders
.matched_variable_decls
.get(search_hir_id)
{
let code = self.hir_id_snippet(matched_var_decl.code_hir_id);
if code == matched_var_decl.name {
self.substitute_hir_ids
.insert(hir_id, matched_var_decl.code_hir_id);
self.result.push(CodeSubstitution::new(ident.span, code));
}
}
}
}
intravisit::walk_pat(self, pat);
}
}
pub(crate) fn substitions_for_matches<'r, 'a, 'tcx>(
tcx: TyCtxt<'tcx>,
matches: &Matches<'r, 'tcx>,
) -> Vec<CodeSubstitution<Span>> {
fn add_substitions_for_matches<'r, 'a, 'tcx, T: StartMatch>(
tcx: TyCtxt<'tcx>,
matches: &[Match<'r, 'tcx, T>],
substitutions: &mut Vec<CodeSubstitution<Span>>,
) {
for m in matches {
let replacement_src = m.get_replacement_source(tcx);
substitutions.push(
CodeSubstitution::new(m.original_span, replacement_src)
.needs_parenthesis(T::needs_parenthesis(m.parent_node, &*m.rule.replace)),
);
}
}
let mut substitutions = vec![];
add_substitions_for_matches(tcx, &matches.expr_matches, &mut substitutions);
add_substitions_for_matches(tcx, &matches.pattern_matches, &mut substitutions);
add_substitions_for_matches(tcx, &matches.type_matches, &mut substitutions);
add_substitions_for_matches(tcx, &matches.trait_ref_matches, &mut substitutions);
substitutions.sort();
substitutions
}