mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-04 19:29:07 +00:00
Auto merge of #7938 - camsteffen:visitors, r=xFrednet
Introduce `expr_visitor` and `expr_visitor_no_bodies` changelog: none A couple utils that satisfy a *lot* of visitor use cases. Factoring in every possible usage would be really big so I just focused on cleaning clippy_utils.
This commit is contained in:
commit
94517d397c
@ -1,8 +1,8 @@
|
|||||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||||
use clippy_utils::higher::VecArgs;
|
use clippy_utils::higher::VecArgs;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use clippy_utils::usage::UsedAfterExprVisitor;
|
use clippy_utils::usage::local_used_after_expr;
|
||||||
use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local_id};
|
use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id};
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
@ -118,7 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
|
|||||||
if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
|
if let ty::Closure(_, substs) = callee_ty.peel_refs().kind();
|
||||||
if substs.as_closure().kind() == ClosureKind::FnMut;
|
if substs.as_closure().kind() == ClosureKind::FnMut;
|
||||||
if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
|
if get_enclosing_loop_or_closure(cx.tcx, expr).is_some()
|
||||||
|| UsedAfterExprVisitor::is_found(cx, callee);
|
|| path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, callee));
|
||||||
|
|
||||||
then {
|
then {
|
||||||
// Mutable closure is used after current expr; we cannot consume it.
|
// Mutable closure is used after current expr; we cannot consume it.
|
||||||
|
@ -10,7 +10,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
|||||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
|
use rustc_middle::ty::adjustment::{Adjust, Adjustment};
|
||||||
use rustc_middle::ty::Ty;
|
use rustc_middle::ty::Ty;
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::{sym, BytePos, ExpnData, ExpnKind, Span, Symbol};
|
use rustc_span::{sym, ExpnData, ExpnKind, Span, Symbol};
|
||||||
|
|
||||||
declare_clippy_lint! {
|
declare_clippy_lint! {
|
||||||
/// ### What it does
|
/// ### What it does
|
||||||
@ -128,7 +128,7 @@ fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symb
|
|||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
FORMAT_IN_FORMAT_ARGS,
|
FORMAT_IN_FORMAT_ARGS,
|
||||||
trim_semicolon(cx, call_site),
|
call_site,
|
||||||
&format!("`format!` in `{}!` args", name),
|
&format!("`format!` in `{}!` args", name),
|
||||||
|diag| {
|
|diag| {
|
||||||
diag.help(&format!(
|
diag.help(&format!(
|
||||||
@ -192,13 +192,6 @@ fn is_aliased(args: &[FormatArgsArg<'_>], i: usize) -> bool {
|
|||||||
.any(|(j, arg)| i != j && std::ptr::eq(value, arg.value))
|
.any(|(j, arg)| i != j && std::ptr::eq(value, arg.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn trim_semicolon(cx: &LateContext<'_>, span: Span) -> Span {
|
|
||||||
snippet_opt(cx, span).map_or(span, |snippet| {
|
|
||||||
let snippet = snippet.trim_end_matches(';');
|
|
||||||
span.with_hi(span.lo() + BytePos(u32::try_from(snippet.len()).unwrap()))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
|
fn count_needed_derefs<'tcx, I>(mut ty: Ty<'tcx>, mut iter: I) -> (usize, Ty<'tcx>)
|
||||||
where
|
where
|
||||||
I: Iterator<Item = &'tcx Adjustment<'tcx>>,
|
I: Iterator<Item = &'tcx Adjustment<'tcx>>,
|
||||||
|
@ -2,10 +2,10 @@ use clippy_utils::{
|
|||||||
diagnostics::span_lint_and_sugg,
|
diagnostics::span_lint_and_sugg,
|
||||||
get_async_fn_body, is_async_fn,
|
get_async_fn_body, is_async_fn,
|
||||||
source::{snippet_with_applicability, snippet_with_context, walk_span_to_context},
|
source::{snippet_with_applicability, snippet_with_context, walk_span_to_context},
|
||||||
visitors::visit_break_exprs,
|
visitors::expr_visitor_no_bodies,
|
||||||
};
|
};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::intravisit::FnKind;
|
use rustc_hir::intravisit::{FnKind, Visitor};
|
||||||
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
|
use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
|
||||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
@ -144,20 +144,24 @@ fn lint_implicit_returns(
|
|||||||
|
|
||||||
ExprKind::Loop(block, ..) => {
|
ExprKind::Loop(block, ..) => {
|
||||||
let mut add_return = false;
|
let mut add_return = false;
|
||||||
visit_break_exprs(block, |break_expr, dest, sub_expr| {
|
expr_visitor_no_bodies(|e| {
|
||||||
if dest.target_id.ok() == Some(expr.hir_id) {
|
if let ExprKind::Break(dest, sub_expr) = e.kind {
|
||||||
if call_site_span.is_none() && break_expr.span.ctxt() == ctxt {
|
if dest.target_id.ok() == Some(expr.hir_id) {
|
||||||
// At this point sub_expr can be `None` in async functions which either diverge, or return the
|
if call_site_span.is_none() && e.span.ctxt() == ctxt {
|
||||||
// unit type.
|
// At this point sub_expr can be `None` in async functions which either diverge, or return
|
||||||
if let Some(sub_expr) = sub_expr {
|
// the unit type.
|
||||||
lint_break(cx, break_expr.span, sub_expr.span);
|
if let Some(sub_expr) = sub_expr {
|
||||||
|
lint_break(cx, e.span, sub_expr.span);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// the break expression is from a macro call, add a return to the loop
|
||||||
|
add_return = true;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// the break expression is from a macro call, add a return to the loop
|
|
||||||
add_return = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
true
|
||||||
|
})
|
||||||
|
.visit_block(block);
|
||||||
if add_return {
|
if add_return {
|
||||||
#[allow(clippy::option_if_let_else)]
|
#[allow(clippy::option_if_let_else)]
|
||||||
if let Some(span) = call_site_span {
|
if let Some(span) = call_site_span {
|
||||||
|
@ -632,9 +632,6 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
|||||||
if let ExprKind::Match(ex, arms, _) = expr.kind {
|
if let ExprKind::Match(ex, arms, _) = expr.kind {
|
||||||
check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr);
|
check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr);
|
||||||
}
|
}
|
||||||
if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(cx, expr) {
|
|
||||||
check_match_ref_pats(cx, let_expr, once(let_pat), expr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
|
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#![feature(box_patterns)]
|
#![feature(box_patterns)]
|
||||||
#![feature(in_band_lifetimes)]
|
#![feature(in_band_lifetimes)]
|
||||||
#![feature(iter_zip)]
|
#![feature(iter_zip)]
|
||||||
|
#![feature(let_else)]
|
||||||
#![feature(rustc_private)]
|
#![feature(rustc_private)]
|
||||||
#![feature(control_flow_enum)]
|
#![feature(control_flow_enum)]
|
||||||
#![recursion_limit = "512"]
|
#![recursion_limit = "512"]
|
||||||
@ -68,7 +69,7 @@ use rustc_hir as hir;
|
|||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_hir::hir_id::{HirIdMap, HirIdSet};
|
use rustc_hir::hir_id::{HirIdMap, HirIdSet};
|
||||||
use rustc_hir::intravisit::{self, walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
|
use rustc_hir::intravisit::{walk_expr, ErasedMap, FnKind, NestedVisitorMap, Visitor};
|
||||||
use rustc_hir::itemlikevisit::ItemLikeVisitor;
|
use rustc_hir::itemlikevisit::ItemLikeVisitor;
|
||||||
use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
|
use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
@ -96,6 +97,7 @@ use rustc_target::abi::Integer;
|
|||||||
|
|
||||||
use crate::consts::{constant, Constant};
|
use crate::consts::{constant, Constant};
|
||||||
use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type};
|
use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type};
|
||||||
|
use crate::visitors::expr_visitor_no_bodies;
|
||||||
|
|
||||||
pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
|
pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
|
||||||
if let Ok(version) = RustcVersion::parse(msrv) {
|
if let Ok(version) = RustcVersion::parse(msrv) {
|
||||||
@ -1107,63 +1109,30 @@ pub fn contains_name(name: Symbol, expr: &Expr<'_>) -> bool {
|
|||||||
|
|
||||||
/// Returns `true` if `expr` contains a return expression
|
/// Returns `true` if `expr` contains a return expression
|
||||||
pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
|
pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
|
||||||
struct RetCallFinder {
|
let mut found = false;
|
||||||
found: bool,
|
expr_visitor_no_bodies(|expr| {
|
||||||
}
|
if !found {
|
||||||
|
|
||||||
impl<'tcx> hir::intravisit::Visitor<'tcx> for RetCallFinder {
|
|
||||||
type Map = Map<'tcx>;
|
|
||||||
|
|
||||||
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
|
|
||||||
if self.found {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if let hir::ExprKind::Ret(..) = &expr.kind {
|
if let hir::ExprKind::Ret(..) = &expr.kind {
|
||||||
self.found = true;
|
found = true;
|
||||||
} else {
|
|
||||||
hir::intravisit::walk_expr(self, expr);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
!found
|
||||||
fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> {
|
})
|
||||||
hir::intravisit::NestedVisitorMap::None
|
.visit_expr(expr);
|
||||||
}
|
found
|
||||||
}
|
|
||||||
|
|
||||||
let mut visitor = RetCallFinder { found: false };
|
|
||||||
visitor.visit_expr(expr);
|
|
||||||
visitor.found
|
|
||||||
}
|
|
||||||
|
|
||||||
struct FindMacroCalls<'a, 'b> {
|
|
||||||
names: &'a [&'b str],
|
|
||||||
result: Vec<Span>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'b, 'tcx> Visitor<'tcx> for FindMacroCalls<'a, 'b> {
|
|
||||||
type Map = Map<'tcx>;
|
|
||||||
|
|
||||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
|
||||||
if self.names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
|
|
||||||
self.result.push(expr.span);
|
|
||||||
}
|
|
||||||
// and check sub-expressions
|
|
||||||
intravisit::walk_expr(self, expr);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
|
||||||
NestedVisitorMap::None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds calls of the specified macros in a function body.
|
/// Finds calls of the specified macros in a function body.
|
||||||
pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> {
|
pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> {
|
||||||
let mut fmc = FindMacroCalls {
|
let mut result = Vec::new();
|
||||||
names,
|
expr_visitor_no_bodies(|expr| {
|
||||||
result: Vec::new(),
|
if names.iter().any(|fun| is_expn_of(expr.span, fun).is_some()) {
|
||||||
};
|
result.push(expr.span);
|
||||||
fmc.visit_expr(&body.value);
|
}
|
||||||
fmc.result
|
true
|
||||||
|
})
|
||||||
|
.visit_expr(&body.value);
|
||||||
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extends the span to the beginning of the spans line, incl. whitespaces.
|
/// Extends the span to the beginning of the spans line, incl. whitespaces.
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use crate::source::snippet;
|
use crate::source::snippet;
|
||||||
|
use crate::visitors::expr_visitor_no_bodies;
|
||||||
use crate::{path_to_local_id, strip_pat_refs};
|
use crate::{path_to_local_id, strip_pat_refs};
|
||||||
use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
|
use rustc_hir::intravisit::Visitor;
|
||||||
use rustc_hir::{Body, BodyId, Expr, ExprKind, HirId, PatKind};
|
use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::hir::map::Map;
|
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
@ -30,50 +30,28 @@ fn extract_clone_suggestions<'tcx>(
|
|||||||
replace: &[(&'static str, &'static str)],
|
replace: &[(&'static str, &'static str)],
|
||||||
body: &'tcx Body<'_>,
|
body: &'tcx Body<'_>,
|
||||||
) -> Option<Vec<(Span, Cow<'static, str>)>> {
|
) -> Option<Vec<(Span, Cow<'static, str>)>> {
|
||||||
let mut visitor = PtrCloneVisitor {
|
let mut abort = false;
|
||||||
cx,
|
let mut spans = Vec::new();
|
||||||
id,
|
expr_visitor_no_bodies(|expr| {
|
||||||
replace,
|
if abort {
|
||||||
spans: vec![],
|
return false;
|
||||||
abort: false,
|
|
||||||
};
|
|
||||||
visitor.visit_body(body);
|
|
||||||
if visitor.abort { None } else { Some(visitor.spans) }
|
|
||||||
}
|
|
||||||
|
|
||||||
struct PtrCloneVisitor<'a, 'tcx> {
|
|
||||||
cx: &'a LateContext<'tcx>,
|
|
||||||
id: HirId,
|
|
||||||
replace: &'a [(&'static str, &'static str)],
|
|
||||||
spans: Vec<(Span, Cow<'static, str>)>,
|
|
||||||
abort: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'tcx> Visitor<'tcx> for PtrCloneVisitor<'a, 'tcx> {
|
|
||||||
type Map = Map<'tcx>;
|
|
||||||
|
|
||||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
|
||||||
if self.abort {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if let ExprKind::MethodCall(seg, _, [recv], _) = expr.kind {
|
if let ExprKind::MethodCall(seg, _, [recv], _) = expr.kind {
|
||||||
if path_to_local_id(recv, self.id) {
|
if path_to_local_id(recv, id) {
|
||||||
if seg.ident.name.as_str() == "capacity" {
|
if seg.ident.name.as_str() == "capacity" {
|
||||||
self.abort = true;
|
abort = true;
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
for &(fn_name, suffix) in self.replace {
|
for &(fn_name, suffix) in replace {
|
||||||
if seg.ident.name.as_str() == fn_name {
|
if seg.ident.name.as_str() == fn_name {
|
||||||
self.spans.push((expr.span, snippet(self.cx, recv.span, "_") + suffix));
|
spans.push((expr.span, snippet(cx, recv.span, "_") + suffix));
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
walk_expr(self, expr);
|
!abort
|
||||||
}
|
})
|
||||||
|
.visit_body(body);
|
||||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
if abort { None } else { Some(spans) }
|
||||||
NestedVisitorMap::None
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate as utils;
|
use crate as utils;
|
||||||
|
use crate::visitors::{expr_visitor, expr_visitor_no_bodies};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::intravisit;
|
use rustc_hir::intravisit::{self, Visitor};
|
||||||
use rustc_hir::intravisit::{NestedVisitorMap, Visitor};
|
|
||||||
use rustc_hir::HirIdSet;
|
use rustc_hir::HirIdSet;
|
||||||
use rustc_hir::{Expr, ExprKind, HirId};
|
use rustc_hir::{Expr, ExprKind, HirId};
|
||||||
use rustc_infer::infer::TyCtxtInferExt;
|
use rustc_infer::infer::TyCtxtInferExt;
|
||||||
@ -148,96 +148,47 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ReturnBreakContinueMacroVisitor {
|
pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
|
||||||
seen_return_break_continue: bool,
|
let mut seen_return_break_continue = false;
|
||||||
}
|
expr_visitor_no_bodies(|ex| {
|
||||||
|
if seen_return_break_continue {
|
||||||
impl ReturnBreakContinueMacroVisitor {
|
return false;
|
||||||
fn new() -> ReturnBreakContinueMacroVisitor {
|
|
||||||
ReturnBreakContinueMacroVisitor {
|
|
||||||
seen_return_break_continue: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'tcx> Visitor<'tcx> for ReturnBreakContinueMacroVisitor {
|
|
||||||
type Map = Map<'tcx>;
|
|
||||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
|
||||||
NestedVisitorMap::None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
|
|
||||||
if self.seen_return_break_continue {
|
|
||||||
// No need to look farther if we've already seen one of them
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
match &ex.kind {
|
match &ex.kind {
|
||||||
ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
|
ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
|
||||||
self.seen_return_break_continue = true;
|
seen_return_break_continue = true;
|
||||||
},
|
},
|
||||||
// Something special could be done here to handle while or for loop
|
// Something special could be done here to handle while or for loop
|
||||||
// desugaring, as this will detect a break if there's a while loop
|
// desugaring, as this will detect a break if there's a while loop
|
||||||
// or a for loop inside the expression.
|
// or a for loop inside the expression.
|
||||||
_ => {
|
_ => {
|
||||||
if ex.span.from_expansion() {
|
if ex.span.from_expansion() {
|
||||||
self.seen_return_break_continue = true;
|
seen_return_break_continue = true;
|
||||||
} else {
|
|
||||||
rustc_hir::intravisit::walk_expr(self, ex);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
!seen_return_break_continue
|
||||||
|
})
|
||||||
|
.visit_expr(expression);
|
||||||
|
seen_return_break_continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
|
pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool {
|
||||||
let mut recursive_visitor = ReturnBreakContinueMacroVisitor::new();
|
let Some(block) = utils::get_enclosing_block(cx, local_id) else { return false };
|
||||||
recursive_visitor.visit_expr(expression);
|
let mut used_after_expr = false;
|
||||||
recursive_visitor.seen_return_break_continue
|
let mut past_expr = false;
|
||||||
}
|
expr_visitor(cx, |expr| {
|
||||||
|
if used_after_expr {
|
||||||
pub struct UsedAfterExprVisitor<'a, 'tcx> {
|
return false;
|
||||||
cx: &'a LateContext<'tcx>,
|
|
||||||
expr: &'tcx Expr<'tcx>,
|
|
||||||
definition: HirId,
|
|
||||||
past_expr: bool,
|
|
||||||
used_after_expr: bool,
|
|
||||||
}
|
|
||||||
impl<'a, 'tcx> UsedAfterExprVisitor<'a, 'tcx> {
|
|
||||||
pub fn is_found(cx: &'a LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
|
||||||
utils::path_to_local(expr).map_or(false, |definition| {
|
|
||||||
let mut visitor = UsedAfterExprVisitor {
|
|
||||||
cx,
|
|
||||||
expr,
|
|
||||||
definition,
|
|
||||||
past_expr: false,
|
|
||||||
used_after_expr: false,
|
|
||||||
};
|
|
||||||
utils::get_enclosing_block(cx, definition).map_or(false, |block| {
|
|
||||||
visitor.visit_block(block);
|
|
||||||
visitor.used_after_expr
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, 'tcx> intravisit::Visitor<'tcx> for UsedAfterExprVisitor<'a, 'tcx> {
|
|
||||||
type Map = Map<'tcx>;
|
|
||||||
|
|
||||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
|
||||||
NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
|
||||||
if self.used_after_expr {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if expr.hir_id == self.expr.hir_id {
|
if expr.hir_id == after.hir_id {
|
||||||
self.past_expr = true;
|
past_expr = true;
|
||||||
} else if self.past_expr && utils::path_to_local_id(expr, self.definition) {
|
} else if past_expr && utils::path_to_local_id(expr, local_id) {
|
||||||
self.used_after_expr = true;
|
used_after_expr = true;
|
||||||
} else {
|
|
||||||
intravisit::walk_expr(self, expr);
|
|
||||||
}
|
}
|
||||||
}
|
!used_after_expr
|
||||||
|
})
|
||||||
|
.visit_block(block);
|
||||||
|
used_after_expr
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,66 @@
|
|||||||
use crate::path_to_local_id;
|
use crate::path_to_local_id;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::intravisit::{self, walk_expr, ErasedMap, NestedVisitorMap, Visitor};
|
use rustc_hir::intravisit::{self, walk_expr, NestedVisitorMap, Visitor};
|
||||||
use rustc_hir::{def::Res, Arm, Block, Body, BodyId, Destination, Expr, ExprKind, HirId, Stmt};
|
use rustc_hir::{def::Res, Arm, Block, Body, BodyId, Expr, ExprKind, HirId, Stmt};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::hir::map::Map;
|
use rustc_middle::hir::map::Map;
|
||||||
use std::ops::ControlFlow;
|
|
||||||
|
/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
|
||||||
|
/// bodies (i.e. closures) are visited.
|
||||||
|
/// If the callback returns `true`, the expr just provided to the callback is walked.
|
||||||
|
#[must_use]
|
||||||
|
pub fn expr_visitor<'tcx>(cx: &LateContext<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
|
||||||
|
struct V<'tcx, F> {
|
||||||
|
hir: Map<'tcx>,
|
||||||
|
f: F,
|
||||||
|
}
|
||||||
|
impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<'tcx, F> {
|
||||||
|
type Map = Map<'tcx>;
|
||||||
|
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||||
|
NestedVisitorMap::OnlyBodies(self.hir)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
||||||
|
if (self.f)(expr) {
|
||||||
|
walk_expr(self, expr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
V { hir: cx.tcx.hir(), f }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
|
||||||
|
/// bodies (i.e. closures) are not visited.
|
||||||
|
/// If the callback returns `true`, the expr just provided to the callback is walked.
|
||||||
|
#[must_use]
|
||||||
|
pub fn expr_visitor_no_bodies<'tcx>(f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
|
||||||
|
struct V<F>(F);
|
||||||
|
impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<F> {
|
||||||
|
type Map = intravisit::ErasedMap<'tcx>;
|
||||||
|
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
||||||
|
NestedVisitorMap::None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
||||||
|
if (self.0)(e) {
|
||||||
|
walk_expr(self, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
V(f)
|
||||||
|
}
|
||||||
|
|
||||||
/// returns `true` if expr contains match expr desugared from try
|
/// returns `true` if expr contains match expr desugared from try
|
||||||
fn contains_try(expr: &hir::Expr<'_>) -> bool {
|
fn contains_try(expr: &hir::Expr<'_>) -> bool {
|
||||||
struct TryFinder {
|
let mut found = false;
|
||||||
found: bool,
|
expr_visitor_no_bodies(|e| {
|
||||||
}
|
if !found {
|
||||||
|
found = matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar));
|
||||||
impl<'hir> intravisit::Visitor<'hir> for TryFinder {
|
|
||||||
type Map = Map<'hir>;
|
|
||||||
|
|
||||||
fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<Self::Map> {
|
|
||||||
intravisit::NestedVisitorMap::None
|
|
||||||
}
|
}
|
||||||
|
!found
|
||||||
fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
|
})
|
||||||
if self.found {
|
.visit_expr(expr);
|
||||||
return;
|
found
|
||||||
}
|
|
||||||
match expr.kind {
|
|
||||||
hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar) => self.found = true,
|
|
||||||
_ => intravisit::walk_expr(self, expr),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut visitor = TryFinder { found: false };
|
|
||||||
visitor.visit_expr(expr);
|
|
||||||
visitor.found
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
|
pub fn find_all_ret_expressions<'hir, F>(_cx: &LateContext<'_>, expr: &'hir hir::Expr<'hir>, callback: F) -> bool
|
||||||
@ -165,103 +193,35 @@ visitable_ref!(Stmt, visit_stmt);
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
/// Calls the given function for each break expression.
|
|
||||||
pub fn visit_break_exprs<'tcx>(
|
|
||||||
node: impl Visitable<'tcx>,
|
|
||||||
f: impl FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>),
|
|
||||||
) {
|
|
||||||
struct V<F>(F);
|
|
||||||
impl<'tcx, F: FnMut(&'tcx Expr<'tcx>, Destination, Option<&'tcx Expr<'tcx>>)> Visitor<'tcx> for V<F> {
|
|
||||||
type Map = ErasedMap<'tcx>;
|
|
||||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
|
||||||
NestedVisitorMap::None
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
|
||||||
if let ExprKind::Break(dest, sub_expr) = e.kind {
|
|
||||||
self.0(e, dest, sub_expr);
|
|
||||||
}
|
|
||||||
walk_expr(self, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
node.visit(&mut V(f));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks if the given resolved path is used in the given body.
|
/// Checks if the given resolved path is used in the given body.
|
||||||
pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
|
pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool {
|
||||||
struct V<'a, 'tcx> {
|
let mut found = false;
|
||||||
cx: &'a LateContext<'tcx>,
|
expr_visitor(cx, |e| {
|
||||||
res: Res,
|
if found {
|
||||||
found: bool,
|
return false;
|
||||||
}
|
|
||||||
impl Visitor<'tcx> for V<'_, 'tcx> {
|
|
||||||
type Map = Map<'tcx>;
|
|
||||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
|
||||||
NestedVisitorMap::OnlyBodies(self.cx.tcx.hir())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
if let ExprKind::Path(p) = &e.kind {
|
||||||
if self.found {
|
if cx.qpath_res(p, e.hir_id) == res {
|
||||||
return;
|
found = true;
|
||||||
}
|
|
||||||
|
|
||||||
if let ExprKind::Path(p) = &e.kind {
|
|
||||||
if self.cx.qpath_res(p, e.hir_id) == self.res {
|
|
||||||
self.found = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
walk_expr(self, e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
!found
|
||||||
|
})
|
||||||
let mut v = V { cx, res, found: false };
|
.visit_expr(&cx.tcx.hir().body(body).value);
|
||||||
v.visit_expr(&cx.tcx.hir().body(body).value);
|
found
|
||||||
v.found
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calls the given function for each usage of the given local.
|
|
||||||
pub fn for_each_local_usage<'tcx, B>(
|
|
||||||
cx: &LateContext<'tcx>,
|
|
||||||
visitable: impl Visitable<'tcx>,
|
|
||||||
id: HirId,
|
|
||||||
f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
|
|
||||||
) -> ControlFlow<B> {
|
|
||||||
struct V<'tcx, B, F> {
|
|
||||||
map: Map<'tcx>,
|
|
||||||
id: HirId,
|
|
||||||
f: F,
|
|
||||||
res: ControlFlow<B>,
|
|
||||||
}
|
|
||||||
impl<'tcx, B, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>> Visitor<'tcx> for V<'tcx, B, F> {
|
|
||||||
type Map = Map<'tcx>;
|
|
||||||
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
|
|
||||||
NestedVisitorMap::OnlyBodies(self.map)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
|
||||||
if self.res.is_continue() {
|
|
||||||
if path_to_local_id(e, self.id) {
|
|
||||||
self.res = (self.f)(e);
|
|
||||||
} else {
|
|
||||||
walk_expr(self, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut v = V {
|
|
||||||
map: cx.tcx.hir(),
|
|
||||||
id,
|
|
||||||
f,
|
|
||||||
res: ControlFlow::CONTINUE,
|
|
||||||
};
|
|
||||||
visitable.visit(&mut v);
|
|
||||||
v.res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the given local is used.
|
/// Checks if the given local is used.
|
||||||
pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
|
pub fn is_local_used(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
|
||||||
for_each_local_usage(cx, visitable, id, |_| ControlFlow::BREAK).is_break()
|
let mut is_used = false;
|
||||||
|
let mut visitor = expr_visitor(cx, |expr| {
|
||||||
|
if !is_used {
|
||||||
|
is_used = path_to_local_id(expr, id);
|
||||||
|
}
|
||||||
|
!is_used
|
||||||
|
});
|
||||||
|
visitable.visit(&mut visitor);
|
||||||
|
drop(visitor);
|
||||||
|
is_used
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user