Replace expr_visitor
with for_each_expr
This commit is contained in:
parent
bef93d3b14
commit
649d443646
@ -2,10 +2,11 @@
|
|||||||
diagnostics::span_lint_hir_and_then,
|
diagnostics::span_lint_hir_and_then,
|
||||||
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::expr_visitor_no_bodies,
|
visitors::for_each_expr,
|
||||||
};
|
};
|
||||||
|
use core::ops::ControlFlow;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::intravisit::{FnKind, Visitor};
|
use rustc_hir::intravisit::FnKind;
|
||||||
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;
|
||||||
@ -152,7 +153,7 @@ fn lint_implicit_returns(
|
|||||||
|
|
||||||
ExprKind::Loop(block, ..) => {
|
ExprKind::Loop(block, ..) => {
|
||||||
let mut add_return = false;
|
let mut add_return = false;
|
||||||
expr_visitor_no_bodies(|e| {
|
let _: Option<!> = for_each_expr(block, |e| {
|
||||||
if let ExprKind::Break(dest, sub_expr) = e.kind {
|
if let ExprKind::Break(dest, sub_expr) = e.kind {
|
||||||
if dest.target_id.ok() == Some(expr.hir_id) {
|
if dest.target_id.ok() == Some(expr.hir_id) {
|
||||||
if call_site_span.is_none() && e.span.ctxt() == ctxt {
|
if call_site_span.is_none() && e.span.ctxt() == ctxt {
|
||||||
@ -167,9 +168,8 @@ fn lint_implicit_returns(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
true
|
ControlFlow::Continue(())
|
||||||
})
|
});
|
||||||
.visit_block(block);
|
|
||||||
if add_return {
|
if add_return {
|
||||||
#[expect(clippy::option_if_let_else)]
|
#[expect(clippy::option_if_let_else)]
|
||||||
if let Some(span) = call_site_span {
|
if let Some(span) = call_site_span {
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
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::source::snippet_with_context;
|
use clippy_utils::source::snippet_with_context;
|
||||||
use clippy_utils::usage::local_used_after_expr;
|
use clippy_utils::usage::local_used_after_expr;
|
||||||
use clippy_utils::visitors::expr_visitor;
|
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
|
||||||
use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths};
|
use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths};
|
||||||
|
use core::ops::ControlFlow;
|
||||||
use if_chain::if_chain;
|
use if_chain::if_chain;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::intravisit::Visitor;
|
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind,
|
BindingAnnotation, Expr, ExprKind, HirId, LangItem, Local, MatchSource, Node, Pat, PatKind, QPath, Stmt, StmtKind,
|
||||||
};
|
};
|
||||||
@ -211,7 +211,7 @@ fn indirect_usage<'tcx>(
|
|||||||
binding: HirId,
|
binding: HirId,
|
||||||
ctxt: SyntaxContext,
|
ctxt: SyntaxContext,
|
||||||
) -> Option<IndirectUsage<'tcx>> {
|
) -> Option<IndirectUsage<'tcx>> {
|
||||||
if let StmtKind::Local(Local {
|
if let StmtKind::Local(&Local {
|
||||||
pat: Pat {
|
pat: Pat {
|
||||||
kind: PatKind::Binding(BindingAnnotation::NONE, _, ident, None),
|
kind: PatKind::Binding(BindingAnnotation::NONE, _, ident, None),
|
||||||
..
|
..
|
||||||
@ -222,14 +222,12 @@ fn indirect_usage<'tcx>(
|
|||||||
}) = stmt.kind
|
}) = stmt.kind
|
||||||
{
|
{
|
||||||
let mut path_to_binding = None;
|
let mut path_to_binding = None;
|
||||||
expr_visitor(cx, |expr| {
|
let _: Option<!> = for_each_expr_with_closures(cx, init_expr, |e| {
|
||||||
if path_to_local_id(expr, binding) {
|
if path_to_local_id(e, binding) {
|
||||||
path_to_binding = Some(expr);
|
path_to_binding = Some(e);
|
||||||
}
|
}
|
||||||
|
ControlFlow::Continue(Descend::from(path_to_binding.is_none()))
|
||||||
path_to_binding.is_none()
|
});
|
||||||
})
|
|
||||||
.visit_expr(init_expr);
|
|
||||||
|
|
||||||
let mut parents = cx.tcx.hir().parent_iter(path_to_binding?.hir_id);
|
let mut parents = cx.tcx.hir().parent_iter(path_to_binding?.hir_id);
|
||||||
let iter_usage = parse_iter_usage(cx, ctxt, &mut parents)?;
|
let iter_usage = parse_iter_usage(cx, ctxt, &mut parents)?;
|
||||||
@ -250,7 +248,7 @@ fn indirect_usage<'tcx>(
|
|||||||
..
|
..
|
||||||
} = iter_usage
|
} = iter_usage
|
||||||
{
|
{
|
||||||
if parent_id == *local_hir_id {
|
if parent_id == local_hir_id {
|
||||||
return Some(IndirectUsage {
|
return Some(IndirectUsage {
|
||||||
name: ident.name,
|
name: ident.name,
|
||||||
span: stmt.span,
|
span: stmt.span,
|
||||||
|
@ -2,9 +2,9 @@
|
|||||||
use clippy_utils::path_to_local;
|
use clippy_utils::path_to_local;
|
||||||
use clippy_utils::source::snippet_opt;
|
use clippy_utils::source::snippet_opt;
|
||||||
use clippy_utils::ty::needs_ordered_drop;
|
use clippy_utils::ty::needs_ordered_drop;
|
||||||
use clippy_utils::visitors::{expr_visitor, expr_visitor_no_bodies, is_local_used};
|
use clippy_utils::visitors::{for_each_expr, for_each_expr_with_closures, is_local_used};
|
||||||
|
use core::ops::ControlFlow;
|
||||||
use rustc_errors::{Applicability, MultiSpan};
|
use rustc_errors::{Applicability, MultiSpan};
|
||||||
use rustc_hir::intravisit::Visitor;
|
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt,
|
BindingAnnotation, Block, Expr, ExprKind, HirId, Local, LocalSource, MatchSource, Node, Pat, PatKind, Stmt,
|
||||||
StmtKind,
|
StmtKind,
|
||||||
@ -64,31 +64,25 @@
|
|||||||
declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]);
|
declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]);
|
||||||
|
|
||||||
fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool {
|
fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool {
|
||||||
let mut seen = false;
|
for_each_expr_with_closures(cx, stmt, |e| {
|
||||||
expr_visitor(cx, |expr| {
|
if matches!(e.kind, ExprKind::Assign(..)) {
|
||||||
if let ExprKind::Assign(..) = expr.kind {
|
ControlFlow::Break(())
|
||||||
seen = true;
|
} else {
|
||||||
|
ControlFlow::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
!seen
|
|
||||||
})
|
})
|
||||||
.visit_stmt(stmt);
|
.is_some()
|
||||||
|
|
||||||
seen
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contains_let(cond: &Expr<'_>) -> bool {
|
fn contains_let(cond: &Expr<'_>) -> bool {
|
||||||
let mut seen = false;
|
for_each_expr(cond, |e| {
|
||||||
expr_visitor_no_bodies(|expr| {
|
if matches!(e.kind, ExprKind::Let(_)) {
|
||||||
if let ExprKind::Let(_) = expr.kind {
|
ControlFlow::Break(())
|
||||||
seen = true;
|
} else {
|
||||||
|
ControlFlow::Continue(())
|
||||||
}
|
}
|
||||||
|
|
||||||
!seen
|
|
||||||
})
|
})
|
||||||
.visit_expr(cond);
|
.is_some()
|
||||||
|
|
||||||
seen
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
use clippy_utils::macros::root_macro_call_first_node;
|
use clippy_utils::macros::root_macro_call_first_node;
|
||||||
use clippy_utils::return_ty;
|
use clippy_utils::return_ty;
|
||||||
use clippy_utils::ty::is_type_diagnostic_item;
|
use clippy_utils::ty::is_type_diagnostic_item;
|
||||||
use clippy_utils::visitors::expr_visitor_no_bodies;
|
use clippy_utils::visitors::{for_each_expr, Descend};
|
||||||
|
use core::ops::ControlFlow;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::intravisit::{FnKind, Visitor};
|
use rustc_hir::intravisit::FnKind;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||||
use rustc_span::{sym, Span};
|
use rustc_span::{sym, Span};
|
||||||
@ -58,18 +59,20 @@ fn check_fn(
|
|||||||
|
|
||||||
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
|
fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) {
|
||||||
let mut panics = Vec::new();
|
let mut panics = Vec::new();
|
||||||
expr_visitor_no_bodies(|expr| {
|
let _: Option<!> = for_each_expr(body.value, |e| {
|
||||||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true };
|
let Some(macro_call) = root_macro_call_first_node(cx, e) else {
|
||||||
|
return ControlFlow::Continue(Descend::Yes);
|
||||||
|
};
|
||||||
if matches!(
|
if matches!(
|
||||||
cx.tcx.item_name(macro_call.def_id).as_str(),
|
cx.tcx.item_name(macro_call.def_id).as_str(),
|
||||||
"unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne"
|
"unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne"
|
||||||
) {
|
) {
|
||||||
panics.push(macro_call.span);
|
panics.push(macro_call.span);
|
||||||
return false;
|
ControlFlow::Continue(Descend::No)
|
||||||
|
} else {
|
||||||
|
ControlFlow::Continue(Descend::Yes)
|
||||||
}
|
}
|
||||||
true
|
});
|
||||||
})
|
|
||||||
.visit_expr(body.value);
|
|
||||||
if !panics.is_empty() {
|
if !panics.is_empty() {
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
|
@ -2,9 +2,10 @@
|
|||||||
diagnostics::{span_lint, span_lint_and_sugg},
|
diagnostics::{span_lint, span_lint_and_sugg},
|
||||||
higher::{get_vec_init_kind, VecInitKind},
|
higher::{get_vec_init_kind, VecInitKind},
|
||||||
source::snippet,
|
source::snippet,
|
||||||
visitors::expr_visitor_no_bodies,
|
visitors::for_each_expr,
|
||||||
};
|
};
|
||||||
use hir::{intravisit::Visitor, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind};
|
use core::ops::ControlFlow;
|
||||||
|
use hir::{Expr, ExprKind, Local, PatKind, PathSegment, QPath, StmtKind};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
@ -58,10 +59,8 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &hir::Block<'tcx>) {
|
|||||||
&& let PatKind::Binding(_, _, ident, _) = pat.kind
|
&& let PatKind::Binding(_, _, ident, _) = pat.kind
|
||||||
&& let Some(vec_init_kind) = get_vec_init_kind(cx, init)
|
&& let Some(vec_init_kind) = get_vec_init_kind(cx, init)
|
||||||
{
|
{
|
||||||
// finds use of `_.read(&mut v)`
|
let visitor = |expr: &Expr<'_>| {
|
||||||
let mut read_found = false;
|
if let ExprKind::MethodCall(path, _, [arg], _) = expr.kind
|
||||||
let mut visitor = expr_visitor_no_bodies(|expr| {
|
|
||||||
if let ExprKind::MethodCall(path, _self, [arg], _) = expr.kind
|
|
||||||
&& let PathSegment { ident: read_or_read_exact, .. } = *path
|
&& let PathSegment { ident: read_or_read_exact, .. } = *path
|
||||||
&& matches!(read_or_read_exact.as_str(), "read" | "read_exact")
|
&& matches!(read_or_read_exact.as_str(), "read" | "read_exact")
|
||||||
&& let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind
|
&& let ExprKind::AddrOf(_, hir::Mutability::Mut, inner) = arg.kind
|
||||||
@ -69,27 +68,22 @@ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &hir::Block<'tcx>) {
|
|||||||
&& let [inner_seg] = inner_path.segments
|
&& let [inner_seg] = inner_path.segments
|
||||||
&& ident.name == inner_seg.ident.name
|
&& ident.name == inner_seg.ident.name
|
||||||
{
|
{
|
||||||
read_found = true;
|
ControlFlow::Break(())
|
||||||
}
|
|
||||||
!read_found
|
|
||||||
});
|
|
||||||
|
|
||||||
let next_stmt_span;
|
|
||||||
if idx == block.stmts.len() - 1 {
|
|
||||||
// case { .. stmt; expr }
|
|
||||||
if let Some(e) = block.expr {
|
|
||||||
visitor.visit_expr(e);
|
|
||||||
next_stmt_span = e.span;
|
|
||||||
} else {
|
} else {
|
||||||
return;
|
ControlFlow::Continue(())
|
||||||
}
|
}
|
||||||
} else {
|
};
|
||||||
|
|
||||||
|
let (read_found, next_stmt_span) =
|
||||||
|
if let Some(next_stmt) = block.stmts.get(idx + 1) {
|
||||||
// case { .. stmt; stmt; .. }
|
// case { .. stmt; stmt; .. }
|
||||||
let next_stmt = &block.stmts[idx + 1];
|
(for_each_expr(next_stmt, visitor).is_some(), next_stmt.span)
|
||||||
visitor.visit_stmt(next_stmt);
|
} else if let Some(e) = block.expr {
|
||||||
next_stmt_span = next_stmt.span;
|
// case { .. stmt; expr }
|
||||||
}
|
(for_each_expr(e, visitor).is_some(), e.span)
|
||||||
drop(visitor);
|
} else {
|
||||||
|
return
|
||||||
|
};
|
||||||
|
|
||||||
if read_found && !next_stmt_span.from_expansion() {
|
if read_found && !next_stmt_span.from_expansion() {
|
||||||
let applicability = Applicability::MaybeIncorrect;
|
let applicability = Applicability::MaybeIncorrect;
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
#![feature(control_flow_enum)]
|
#![feature(control_flow_enum)]
|
||||||
#![feature(let_chains)]
|
#![feature(let_chains)]
|
||||||
#![feature(lint_reasons)]
|
#![feature(lint_reasons)]
|
||||||
|
#![feature(never_type)]
|
||||||
#![feature(once_cell)]
|
#![feature(once_cell)]
|
||||||
#![feature(rustc_private)]
|
#![feature(rustc_private)]
|
||||||
#![recursion_limit = "512"]
|
#![recursion_limit = "512"]
|
||||||
@ -65,6 +66,7 @@
|
|||||||
both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
|
both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use core::ops::ControlFlow;
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::hash::BuildHasherDefault;
|
use std::hash::BuildHasherDefault;
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
@ -113,7 +115,7 @@
|
|||||||
|
|
||||||
use crate::consts::{constant, Constant};
|
use crate::consts::{constant, Constant};
|
||||||
use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
|
use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
|
||||||
use crate::visitors::expr_visitor_no_bodies;
|
use crate::visitors::for_each_expr;
|
||||||
|
|
||||||
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) {
|
||||||
@ -1193,17 +1195,14 @@ 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 {
|
||||||
let mut found = false;
|
for_each_expr(expr, |e| {
|
||||||
expr_visitor_no_bodies(|expr| {
|
if matches!(e.kind, hir::ExprKind::Ret(..)) {
|
||||||
if !found {
|
ControlFlow::Break(())
|
||||||
if let hir::ExprKind::Ret(..) = &expr.kind {
|
} else {
|
||||||
found = true;
|
ControlFlow::Continue(())
|
||||||
}
|
|
||||||
}
|
}
|
||||||
!found
|
|
||||||
})
|
})
|
||||||
.visit_expr(expr);
|
.is_some()
|
||||||
found
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extends the span to the beginning of the spans line, incl. whitespaces.
|
/// Extends the span to the beginning of the spans line, incl. whitespaces.
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::is_path_diagnostic_item;
|
use crate::is_path_diagnostic_item;
|
||||||
use crate::source::snippet_opt;
|
use crate::source::snippet_opt;
|
||||||
use crate::visitors::expr_visitor_no_bodies;
|
use crate::visitors::{for_each_expr, Descend};
|
||||||
|
|
||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use itertools::{izip, Either, Itertools};
|
use itertools::{izip, Either, Itertools};
|
||||||
@ -270,20 +270,19 @@ fn find_assert_args_inner<'a, const N: usize>(
|
|||||||
};
|
};
|
||||||
let mut args = ArrayVec::new();
|
let mut args = ArrayVec::new();
|
||||||
let mut panic_expn = None;
|
let mut panic_expn = None;
|
||||||
expr_visitor_no_bodies(|e| {
|
let _: Option<!> = for_each_expr(expr, |e| {
|
||||||
if args.is_full() {
|
if args.is_full() {
|
||||||
if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() {
|
if panic_expn.is_none() && e.span.ctxt() != expr.span.ctxt() {
|
||||||
panic_expn = PanicExpn::parse(cx, e);
|
panic_expn = PanicExpn::parse(cx, e);
|
||||||
}
|
}
|
||||||
panic_expn.is_none()
|
ControlFlow::Continue(Descend::from(panic_expn.is_none()))
|
||||||
} else if is_assert_arg(cx, e, expn) {
|
} else if is_assert_arg(cx, e, expn) {
|
||||||
args.push(e);
|
args.push(e);
|
||||||
false
|
ControlFlow::Continue(Descend::No)
|
||||||
} else {
|
} else {
|
||||||
true
|
ControlFlow::Continue(Descend::Yes)
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.visit_expr(expr);
|
|
||||||
let args = args.into_inner().ok()?;
|
let args = args.into_inner().ok()?;
|
||||||
// if no `panic!(..)` is found, use `PanicExpn::Empty`
|
// if no `panic!(..)` is found, use `PanicExpn::Empty`
|
||||||
// to indicate that the default assertion message is used
|
// to indicate that the default assertion message is used
|
||||||
@ -297,22 +296,19 @@ fn find_assert_within_debug_assert<'a>(
|
|||||||
expn: ExpnId,
|
expn: ExpnId,
|
||||||
assert_name: Symbol,
|
assert_name: Symbol,
|
||||||
) -> Option<(&'a Expr<'a>, ExpnId)> {
|
) -> Option<(&'a Expr<'a>, ExpnId)> {
|
||||||
let mut found = None;
|
for_each_expr(expr, |e| {
|
||||||
expr_visitor_no_bodies(|e| {
|
if !e.span.from_expansion() {
|
||||||
if found.is_some() || !e.span.from_expansion() {
|
return ControlFlow::Continue(Descend::No);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
let e_expn = e.span.ctxt().outer_expn();
|
let e_expn = e.span.ctxt().outer_expn();
|
||||||
if e_expn == expn {
|
if e_expn == expn {
|
||||||
return true;
|
ControlFlow::Continue(Descend::Yes)
|
||||||
|
} else if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) {
|
||||||
|
ControlFlow::Break((e, e_expn))
|
||||||
|
} else {
|
||||||
|
ControlFlow::Continue(Descend::No)
|
||||||
}
|
}
|
||||||
if e_expn.expn_data().macro_def_id.map(|id| cx.tcx.item_name(id)) == Some(assert_name) {
|
|
||||||
found = Some((e, e_expn));
|
|
||||||
}
|
|
||||||
false
|
|
||||||
})
|
})
|
||||||
.visit_expr(expr);
|
|
||||||
found
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> bool {
|
fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) -> bool {
|
||||||
@ -396,16 +392,14 @@ fn new(cx: &LateContext<'_>, pieces: &Expr<'_>) -> Option<Self> {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let mut parts = Vec::new();
|
let mut parts = Vec::new();
|
||||||
expr_visitor_no_bodies(|expr| {
|
let _: Option<!> = for_each_expr(pieces, |expr| {
|
||||||
if let ExprKind::Lit(lit) = &expr.kind {
|
if let ExprKind::Lit(lit) = &expr.kind
|
||||||
if let LitKind::Str(symbol, _) = lit.node {
|
&& let LitKind::Str(symbol, _) = lit.node
|
||||||
parts.push(symbol);
|
{
|
||||||
}
|
parts.push(symbol);
|
||||||
}
|
}
|
||||||
|
ControlFlow::Continue(())
|
||||||
true
|
});
|
||||||
})
|
|
||||||
.visit_expr(pieces);
|
|
||||||
|
|
||||||
Some(Self {
|
Some(Self {
|
||||||
span,
|
span,
|
||||||
@ -431,7 +425,7 @@ impl<'tcx> FormatArgsValues<'tcx> {
|
|||||||
fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self {
|
fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self {
|
||||||
let mut pos_to_value_index = Vec::new();
|
let mut pos_to_value_index = Vec::new();
|
||||||
let mut value_args = Vec::new();
|
let mut value_args = Vec::new();
|
||||||
expr_visitor_no_bodies(|expr| {
|
let _: Option<!> = for_each_expr(args, |expr| {
|
||||||
if expr.span.ctxt() == args.span.ctxt() {
|
if expr.span.ctxt() == args.span.ctxt() {
|
||||||
// ArgumentV1::new_<format_trait>(<val>)
|
// ArgumentV1::new_<format_trait>(<val>)
|
||||||
// ArgumentV1::from_usize(<val>)
|
// ArgumentV1::from_usize(<val>)
|
||||||
@ -453,16 +447,13 @@ fn new(args: &'tcx Expr<'tcx>, format_string_span: SpanData) -> Self {
|
|||||||
|
|
||||||
pos_to_value_index.push(val_idx);
|
pos_to_value_index.push(val_idx);
|
||||||
}
|
}
|
||||||
|
ControlFlow::Continue(Descend::Yes)
|
||||||
true
|
|
||||||
} else {
|
} else {
|
||||||
// assume that any expr with a differing span is a value
|
// assume that any expr with a differing span is a value
|
||||||
value_args.push(expr);
|
value_args.push(expr);
|
||||||
|
ControlFlow::Continue(Descend::No)
|
||||||
false
|
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
.visit_expr(args);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
value_args,
|
value_args,
|
||||||
@ -866,22 +857,18 @@ pub fn parse(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<Self> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> {
|
pub fn find_nested(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, expn_id: ExpnId) -> Option<Self> {
|
||||||
let mut format_args = None;
|
for_each_expr(expr, |e| {
|
||||||
expr_visitor_no_bodies(|e| {
|
|
||||||
if format_args.is_some() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let e_ctxt = e.span.ctxt();
|
let e_ctxt = e.span.ctxt();
|
||||||
if e_ctxt == expr.span.ctxt() {
|
if e_ctxt == expr.span.ctxt() {
|
||||||
return true;
|
ControlFlow::Continue(Descend::Yes)
|
||||||
|
} else if e_ctxt.outer_expn().is_descendant_of(expn_id)
|
||||||
|
&& let Some(args) = FormatArgsExpn::parse(cx, e)
|
||||||
|
{
|
||||||
|
ControlFlow::Break(args)
|
||||||
|
} else {
|
||||||
|
ControlFlow::Continue(Descend::No)
|
||||||
}
|
}
|
||||||
if e_ctxt.outer_expn().is_descendant_of(expn_id) {
|
|
||||||
format_args = FormatArgsExpn::parse(cx, e);
|
|
||||||
}
|
|
||||||
false
|
|
||||||
})
|
})
|
||||||
.visit_expr(expr);
|
|
||||||
format_args
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Source callsite span of all inputs
|
/// Source callsite span of all inputs
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::source::snippet;
|
use crate::source::snippet;
|
||||||
use crate::visitors::expr_visitor_no_bodies;
|
use crate::visitors::{for_each_expr, Descend};
|
||||||
use crate::{path_to_local_id, strip_pat_refs};
|
use crate::{path_to_local_id, strip_pat_refs};
|
||||||
use rustc_hir::intravisit::Visitor;
|
use core::ops::ControlFlow;
|
||||||
use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind};
|
use rustc_hir::{Body, BodyId, ExprKind, HirId, PatKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
@ -30,28 +30,23 @@ 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 abort = false;
|
|
||||||
let mut spans = Vec::new();
|
let mut spans = Vec::new();
|
||||||
expr_visitor_no_bodies(|expr| {
|
for_each_expr(body, |e| {
|
||||||
if abort {
|
if let ExprKind::MethodCall(seg, recv, [], _) = e.kind
|
||||||
return false;
|
&& path_to_local_id(recv, id)
|
||||||
}
|
{
|
||||||
if let ExprKind::MethodCall(seg, recv, [], _) = expr.kind {
|
if seg.ident.as_str() == "capacity" {
|
||||||
if path_to_local_id(recv, id) {
|
return ControlFlow::Break(());
|
||||||
if seg.ident.name.as_str() == "capacity" {
|
}
|
||||||
abort = true;
|
for &(fn_name, suffix) in replace {
|
||||||
return false;
|
if seg.ident.as_str() == fn_name {
|
||||||
}
|
spans.push((e.span, snippet(cx, recv.span, "_") + suffix));
|
||||||
for &(fn_name, suffix) in replace {
|
return ControlFlow::Continue(Descend::No);
|
||||||
if seg.ident.name.as_str() == fn_name {
|
|
||||||
spans.push((expr.span, snippet(cx, recv.span, "_") + suffix));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
!abort
|
ControlFlow::Continue(Descend::Yes)
|
||||||
})
|
})
|
||||||
.visit_body(body);
|
.is_none()
|
||||||
if abort { None } else { Some(spans) }
|
.then_some(spans)
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate as utils;
|
use crate as utils;
|
||||||
use crate::visitors::{expr_visitor, expr_visitor_no_bodies};
|
use crate::visitors::{for_each_expr, for_each_expr_with_closures, Descend};
|
||||||
|
use core::ops::ControlFlow;
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::intravisit::{self, Visitor};
|
use rustc_hir::intravisit::{self, Visitor};
|
||||||
use rustc_hir::HirIdSet;
|
use rustc_hir::HirIdSet;
|
||||||
@ -148,28 +149,17 @@ fn nested_visit_map(&mut self) -> Self::Map {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
|
pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool {
|
||||||
let mut seen_return_break_continue = false;
|
for_each_expr(expression, |e| {
|
||||||
expr_visitor_no_bodies(|ex| {
|
match e.kind {
|
||||||
if seen_return_break_continue {
|
ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => ControlFlow::Break(()),
|
||||||
return false;
|
|
||||||
}
|
|
||||||
match &ex.kind {
|
|
||||||
ExprKind::Ret(..) | ExprKind::Break(..) | ExprKind::Continue(..) => {
|
|
||||||
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 e.span.from_expansion() => ControlFlow::Break(()),
|
||||||
if ex.span.from_expansion() {
|
_ => ControlFlow::Continue(()),
|
||||||
seen_return_break_continue = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
!seen_return_break_continue
|
|
||||||
})
|
})
|
||||||
.visit_expr(expression);
|
.is_some()
|
||||||
seen_return_break_continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool {
|
pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr<'_>) -> bool {
|
||||||
@ -200,23 +190,16 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut used_after_expr = false;
|
|
||||||
let mut past_expr = false;
|
let mut past_expr = false;
|
||||||
expr_visitor(cx, |expr| {
|
for_each_expr_with_closures(cx, block, |e| {
|
||||||
if used_after_expr {
|
if e.hir_id == after.hir_id {
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if expr.hir_id == after.hir_id {
|
|
||||||
past_expr = true;
|
past_expr = true;
|
||||||
return false;
|
ControlFlow::Continue(Descend::No)
|
||||||
|
} else if past_expr && utils::path_to_local_id(e, local_id) {
|
||||||
|
ControlFlow::Break(())
|
||||||
|
} else {
|
||||||
|
ControlFlow::Continue(Descend::Yes)
|
||||||
}
|
}
|
||||||
|
|
||||||
if past_expr && utils::path_to_local_id(expr, local_id) {
|
|
||||||
used_after_expr = true;
|
|
||||||
}
|
|
||||||
!used_after_expr
|
|
||||||
})
|
})
|
||||||
.visit_block(block);
|
.is_some()
|
||||||
used_after_expr
|
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,13 @@
|
|||||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||||
use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
|
use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath, Stmt, UnOp,
|
AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath,
|
||||||
UnsafeSource, Unsafety,
|
Stmt, UnOp, UnsafeSource, Unsafety,
|
||||||
};
|
};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::hir::map::Map;
|
|
||||||
use rustc_middle::hir::nested_filter;
|
use rustc_middle::hir::nested_filter;
|
||||||
use rustc_middle::ty::adjustment::Adjust;
|
use rustc_middle::ty::adjustment::Adjust;
|
||||||
use rustc_middle::ty::{self, Ty, TypeckResults};
|
use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
mod internal {
|
mod internal {
|
||||||
@ -48,6 +47,26 @@ fn descend(&self) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A type which can be visited.
|
||||||
|
pub trait Visitable<'tcx> {
|
||||||
|
/// Calls the corresponding `visit_*` function on the visitor.
|
||||||
|
fn visit<V: Visitor<'tcx>>(self, visitor: &mut V);
|
||||||
|
}
|
||||||
|
macro_rules! visitable_ref {
|
||||||
|
($t:ident, $f:ident) => {
|
||||||
|
impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
|
||||||
|
fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
|
||||||
|
visitor.$f(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
visitable_ref!(Arm, visit_arm);
|
||||||
|
visitable_ref!(Block, visit_block);
|
||||||
|
visitable_ref!(Body, visit_body);
|
||||||
|
visitable_ref!(Expr, visit_expr);
|
||||||
|
visitable_ref!(Stmt, visit_stmt);
|
||||||
|
|
||||||
/// Calls the given function once for each expression contained. This does not enter any bodies or
|
/// Calls the given function once for each expression contained. This does not enter any bodies or
|
||||||
/// nested items.
|
/// nested items.
|
||||||
pub fn for_each_expr<'tcx, B, C: Continue>(
|
pub fn for_each_expr<'tcx, B, C: Continue>(
|
||||||
@ -82,57 +101,63 @@ fn visit_nested_item(&mut self, _: ItemId) {}
|
|||||||
v.res
|
v.res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
|
/// Calls the given function once for each expression contained. This will enter bodies, but not
|
||||||
/// bodies (i.e. closures) are visited.
|
/// nested items.
|
||||||
/// If the callback returns `true`, the expr just provided to the callback is walked.
|
pub fn for_each_expr_with_closures<'tcx, B, C: Continue>(
|
||||||
#[must_use]
|
cx: &LateContext<'tcx>,
|
||||||
pub fn expr_visitor<'tcx>(cx: &LateContext<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
|
node: impl Visitable<'tcx>,
|
||||||
struct V<'tcx, F> {
|
f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
|
||||||
hir: Map<'tcx>,
|
) -> Option<B> {
|
||||||
|
struct V<'tcx, B, F> {
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
f: F,
|
f: F,
|
||||||
|
res: Option<B>,
|
||||||
}
|
}
|
||||||
impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<'tcx, F> {
|
impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<'tcx, B, F> {
|
||||||
type NestedFilter = nested_filter::OnlyBodies;
|
type NestedFilter = nested_filter::OnlyBodies;
|
||||||
fn nested_visit_map(&mut self) -> Self::Map {
|
fn nested_visit_map(&mut self) -> Self::Map {
|
||||||
self.hir
|
self.tcx.hir()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
|
fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
|
||||||
if (self.f)(expr) {
|
if self.res.is_some() {
|
||||||
walk_expr(self, expr);
|
return;
|
||||||
|
}
|
||||||
|
match (self.f)(e) {
|
||||||
|
ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
|
||||||
|
ControlFlow::Break(b) => self.res = Some(b),
|
||||||
|
ControlFlow::Continue(_) => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
V { hir: cx.tcx.hir(), f }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
|
// Only walk closures
|
||||||
/// bodies (i.e. closures) are not visited.
|
fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
|
||||||
/// If the callback returns `true`, the expr just provided to the callback is walked.
|
// Avoid unnecessary `walk_*` calls.
|
||||||
#[must_use]
|
fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {}
|
||||||
pub fn expr_visitor_no_bodies<'tcx>(f: impl FnMut(&'tcx Expr<'tcx>) -> bool) -> impl Visitor<'tcx> {
|
fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {}
|
||||||
struct V<F>(F);
|
fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {}
|
||||||
impl<'tcx, F: FnMut(&'tcx Expr<'tcx>) -> bool> Visitor<'tcx> for V<F> {
|
// Avoid monomorphising all `visit_*` functions.
|
||||||
fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
|
fn visit_nested_item(&mut self, _: ItemId) {}
|
||||||
if (self.0)(e) {
|
|
||||||
walk_expr(self, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
V(f)
|
let mut v = V {
|
||||||
|
tcx: cx.tcx,
|
||||||
|
f,
|
||||||
|
res: None,
|
||||||
|
};
|
||||||
|
node.visit(&mut v);
|
||||||
|
v.res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 {
|
||||||
let mut found = false;
|
for_each_expr(expr, |e| {
|
||||||
expr_visitor_no_bodies(|e| {
|
if matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar)) {
|
||||||
if !found {
|
ControlFlow::Break(())
|
||||||
found = matches!(e.kind, hir::ExprKind::Match(_, _, hir::MatchSource::TryDesugar));
|
} else {
|
||||||
|
ControlFlow::Continue(())
|
||||||
}
|
}
|
||||||
!found
|
|
||||||
})
|
})
|
||||||
.visit_expr(expr);
|
.is_some()
|
||||||
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
|
||||||
@ -228,68 +253,29 @@ fn visit_expr(&mut self, expr: &'hir hir::Expr<'_>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A type which can be visited.
|
|
||||||
pub trait Visitable<'tcx> {
|
|
||||||
/// Calls the corresponding `visit_*` function on the visitor.
|
|
||||||
fn visit<V: Visitor<'tcx>>(self, visitor: &mut V);
|
|
||||||
}
|
|
||||||
macro_rules! visitable_ref {
|
|
||||||
($t:ident, $f:ident) => {
|
|
||||||
impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
|
|
||||||
fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
|
|
||||||
visitor.$f(self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
visitable_ref!(Arm, visit_arm);
|
|
||||||
visitable_ref!(Block, visit_block);
|
|
||||||
visitable_ref!(Body, visit_body);
|
|
||||||
visitable_ref!(Expr, visit_expr);
|
|
||||||
visitable_ref!(Stmt, visit_stmt);
|
|
||||||
|
|
||||||
// impl<'tcx, I: IntoIterator> Visitable<'tcx> for I
|
|
||||||
// where
|
|
||||||
// I::Item: Visitable<'tcx>,
|
|
||||||
// {
|
|
||||||
// fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
|
|
||||||
// for x in self {
|
|
||||||
// x.visit(visitor);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
/// 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 {
|
||||||
let mut found = false;
|
for_each_expr_with_closures(cx, cx.tcx.hir().body(body).value, |e| {
|
||||||
expr_visitor(cx, |e| {
|
|
||||||
if found {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let ExprKind::Path(p) = &e.kind {
|
if let ExprKind::Path(p) = &e.kind {
|
||||||
if cx.qpath_res(p, e.hir_id) == res {
|
if cx.qpath_res(p, e.hir_id) == res {
|
||||||
found = true;
|
return ControlFlow::Break(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
!found
|
ControlFlow::Continue(())
|
||||||
})
|
})
|
||||||
.visit_expr(cx.tcx.hir().body(body).value);
|
.is_some()
|
||||||
found
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the given local is used.
|
/// Checks if the given local is used.
|
||||||
pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
|
pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool {
|
||||||
let mut is_used = false;
|
for_each_expr_with_closures(cx, visitable, |e| {
|
||||||
let mut visitor = expr_visitor(cx, |expr| {
|
if path_to_local_id(e, id) {
|
||||||
if !is_used {
|
ControlFlow::Break(())
|
||||||
is_used = path_to_local_id(expr, id);
|
} else {
|
||||||
|
ControlFlow::Continue(())
|
||||||
}
|
}
|
||||||
!is_used
|
})
|
||||||
});
|
.is_some()
|
||||||
visitable.visit(&mut visitor);
|
|
||||||
drop(visitor);
|
|
||||||
is_used
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if the given expression is a constant.
|
/// Checks if the given expression is a constant.
|
||||||
|
Loading…
Reference in New Issue
Block a user