mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
Rework expr_use_ctxt into an iterator over successive use sites.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::{ExprUseNode, expr_use_ctxt, is_expr_temporary_value, std_or_core};
|
||||
use clippy_utils::{ExprUseNode, get_expr_use_site, is_expr_temporary_value, std_or_core};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Mutability, Ty, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
@@ -22,13 +22,12 @@ pub(super) fn check<'tcx>(
|
||||
|
||||
if matches!(cast_from.kind(), ty::Ref(..))
|
||||
&& let ty::RawPtr(_, to_mutbl) = cast_to.kind()
|
||||
&& let use_cx = expr_use_ctxt(cx, expr)
|
||||
&& let Some(std_or_core) = std_or_core(cx)
|
||||
{
|
||||
if let ExprKind::AddrOf(_, _, addr_inner) = cast_expr.kind
|
||||
&& is_expr_temporary_value(cx, addr_inner)
|
||||
&& matches!(
|
||||
use_cx.use_node(cx),
|
||||
get_expr_use_site(cx.tcx, cx.typeck_results(), expr.span.ctxt(), expr).use_node(cx),
|
||||
ExprUseNode::LetStmt(_) | ExprUseNode::ConstStatic(_)
|
||||
)
|
||||
{
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use clippy_utils::sugg::has_enclosing_paren;
|
||||
use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs};
|
||||
use clippy_utils::{
|
||||
DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, sym,
|
||||
DefinedTy, ExprUseNode, get_expr_use_site, get_parent_expr, is_block_like, is_from_proc_macro, is_lint_allowed, sym,
|
||||
};
|
||||
use rustc_ast::util::parser::ExprPrecedence;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
@@ -19,7 +19,7 @@
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeckResults};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{Span, Symbol};
|
||||
use rustc_span::{Span, Symbol, SyntaxContext};
|
||||
use std::borrow::Cow;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -276,15 +276,15 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
match (self.state.take(), kind) {
|
||||
(None, kind) => {
|
||||
let expr_ty = typeck.expr_ty(expr);
|
||||
let use_cx = expr_use_ctxt(cx, expr);
|
||||
let adjusted_ty = use_cx.adjustments.last().map_or(expr_ty, |a| a.target);
|
||||
let use_site = get_expr_use_site(cx.tcx, typeck, SyntaxContext::root(), expr);
|
||||
let adjusted_ty = use_site.adjustments.last().map_or(expr_ty, |a| a.target);
|
||||
|
||||
match kind {
|
||||
RefOp::Deref if use_cx.same_ctxt => {
|
||||
let use_node = use_cx.use_node(cx);
|
||||
RefOp::Deref if use_site.same_ctxt => {
|
||||
let use_node = use_site.use_node(cx);
|
||||
let sub_ty = typeck.expr_ty(sub_expr);
|
||||
if let ExprUseNode::FieldAccess(name) = use_node
|
||||
&& !use_cx.moved_before_use
|
||||
&& !use_site.moved_before_use
|
||||
&& !ty_contains_field(sub_ty, name.name)
|
||||
{
|
||||
self.state = Some((
|
||||
@@ -331,9 +331,9 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
},
|
||||
));
|
||||
},
|
||||
RefOp::AddrOf(mutability) if use_cx.same_ctxt => {
|
||||
RefOp::AddrOf(mutability) if use_site.same_ctxt => {
|
||||
// Find the number of times the borrow is auto-derefed.
|
||||
let mut iter = use_cx.adjustments.iter();
|
||||
let mut iter = use_site.adjustments.iter();
|
||||
let mut deref_count = 0usize;
|
||||
let next_adjust = loop {
|
||||
match iter.next() {
|
||||
@@ -350,13 +350,13 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
}
|
||||
};
|
||||
|
||||
let use_node = use_cx.use_node(cx);
|
||||
let use_node = use_site.use_node(cx);
|
||||
let stability = use_node.defined_ty(cx).map_or(TyCoercionStability::None, |ty| {
|
||||
TyCoercionStability::for_defined_ty(cx, ty, use_node.is_return())
|
||||
});
|
||||
let can_auto_borrow = match use_node {
|
||||
ExprUseNode::FieldAccess(_)
|
||||
if !use_cx.moved_before_use && matches!(sub_expr.kind, ExprKind::Field(..)) =>
|
||||
if !use_site.moved_before_use && matches!(sub_expr.kind, ExprKind::Field(..)) =>
|
||||
{
|
||||
// `DerefMut` will not be automatically applied to `ManuallyDrop<_>`
|
||||
// field expressions when the base type is a union and the parent
|
||||
@@ -364,10 +364,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
//
|
||||
// e.g. `&mut x.y.z` where `x` is a union, and accessing `z` requires a
|
||||
// deref through `ManuallyDrop<_>` will not compile.
|
||||
!adjust_derefs_manually_drop(use_cx.adjustments, expr_ty)
|
||||
!adjust_derefs_manually_drop(use_site.adjustments, expr_ty)
|
||||
},
|
||||
ExprUseNode::Callee | ExprUseNode::FieldAccess(_) if !use_cx.moved_before_use => true,
|
||||
ExprUseNode::MethodArg(hir_id, _, 0) if !use_cx.moved_before_use => {
|
||||
ExprUseNode::Callee | ExprUseNode::FieldAccess(_) if !use_site.moved_before_use => true,
|
||||
ExprUseNode::MethodArg(hir_id, _, 0) if !use_site.moved_before_use => {
|
||||
// Check for calls to trait methods where the trait is implemented
|
||||
// on a reference.
|
||||
// Two cases need to be handled:
|
||||
@@ -455,7 +455,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
msg,
|
||||
stability,
|
||||
for_field_access: if let ExprUseNode::FieldAccess(name) = use_node
|
||||
&& !use_cx.moved_before_use
|
||||
&& !use_site.moved_before_use
|
||||
{
|
||||
Some(name.name)
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{ExprUseNode, expr_use_ctxt, numeric_literal};
|
||||
use clippy_utils::{ExprUseNode, get_expr_use_site, numeric_literal};
|
||||
use rustc_ast::ast::{LitFloatType, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
@@ -143,7 +143,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
}
|
||||
} else if digits > max as usize && count_digits(&float_str) < digits {
|
||||
if digits >= self.const_literal_digits_threshold
|
||||
&& matches!(expr_use_ctxt(cx, expr).use_node(cx), ExprUseNode::ConstStatic(_))
|
||||
&& matches!(
|
||||
get_expr_use_site(cx.tcx, cx.typeck_results(), expr.span.ctxt(), expr).use_node(cx),
|
||||
ExprUseNode::ConstStatic(_)
|
||||
)
|
||||
{
|
||||
// If a big enough number of digits is specified and it's a constant
|
||||
// we assume the user is definining a constant, and excessive precision is ok
|
||||
|
||||
@@ -4,13 +4,13 @@
|
||||
use clippy_utils::source::{IntoSpan, SpanRangeExt};
|
||||
use clippy_utils::ty::get_field_by_name;
|
||||
use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures};
|
||||
use clippy_utils::{ExprUseNode, expr_use_ctxt, sym};
|
||||
use clippy_utils::{ExprUseNode, get_expr_use_site, sym};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{BindingMode, BorrowKind, ByRef, ClosureKind, Expr, ExprKind, Mutability, Node, PatKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||
use rustc_span::{DUMMY_SP, Span, Symbol};
|
||||
use rustc_span::{DUMMY_SP, Span, Symbol, SyntaxContext};
|
||||
|
||||
use super::MANUAL_INSPECT;
|
||||
|
||||
@@ -49,7 +49,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
|
||||
// Nested closures don't need to treat returns specially.
|
||||
let _: Option<!> = for_each_expr(cx, cx.tcx.hir_body(c.body).value, |e| {
|
||||
if e.res_local_id() == Some(arg_id) {
|
||||
let (kind, same_ctxt) = check_use(cx, e);
|
||||
let (kind, same_ctxt) = check_use(cx, ctxt, e);
|
||||
match (kind, same_ctxt && e.span.ctxt() == ctxt) {
|
||||
(_, false) | (UseKind::Deref | UseKind::Return(..), true) => {
|
||||
requires_copy = true;
|
||||
@@ -67,7 +67,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
|
||||
} else if matches!(e.kind, ExprKind::Ret(_)) {
|
||||
ret_count += 1;
|
||||
} else if e.res_local_id() == Some(arg_id) {
|
||||
let (kind, same_ctxt) = check_use(cx, e);
|
||||
let (kind, same_ctxt) = check_use(cx, ctxt, e);
|
||||
match (kind, same_ctxt && e.span.ctxt() == ctxt) {
|
||||
(UseKind::Return(..), false) => {
|
||||
return ControlFlow::Break(());
|
||||
@@ -209,8 +209,8 @@ enum UseKind<'tcx> {
|
||||
}
|
||||
|
||||
/// Checks how the value is used, and whether it was used in the same `SyntaxContext`.
|
||||
fn check_use<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (UseKind<'tcx>, bool) {
|
||||
let use_cx = expr_use_ctxt(cx, e);
|
||||
fn check_use<'tcx>(cx: &LateContext<'tcx>, ctxt: SyntaxContext, e: &'tcx Expr<'_>) -> (UseKind<'tcx>, bool) {
|
||||
let use_cx = get_expr_use_site(cx.tcx, cx.typeck_results(), ctxt, e);
|
||||
if use_cx
|
||||
.adjustments
|
||||
.first()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::res::{MaybeDef, MaybeTypeckRes};
|
||||
use clippy_utils::source::{snippet, snippet_with_context};
|
||||
use clippy_utils::{expr_use_ctxt, fn_def_id, std_or_core, sym};
|
||||
use clippy_utils::{fn_def_id, get_expr_use_site, std_or_core, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
@@ -21,7 +21,7 @@ pub(super) fn check<'tcx>(
|
||||
&& let ExprKind::Call(_, [repeat_arg]) = repeat_expr.kind
|
||||
&& let Some(def_id) = fn_def_id(cx, repeat_expr)
|
||||
&& cx.tcx.is_diagnostic_item(sym::iter_repeat, def_id)
|
||||
&& !expr_use_ctxt(cx, expr).is_ty_unified
|
||||
&& !get_expr_use_site(cx.tcx, cx.typeck_results(), expr.span.ctxt(), expr).is_ty_unified
|
||||
&& let Some(std_or_core) = std_or_core(cx)
|
||||
&& msrv.meets(cx, msrvs::REPEAT_N)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath, MaybeTypeckRes};
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::{DefinedTy, ExprUseNode, expr_use_ctxt, peel_blocks, strip_pat_refs};
|
||||
use clippy_utils::{DefinedTy, ExprUseNode, get_expr_use_site, peel_blocks, strip_pat_refs};
|
||||
use rustc_ast::ast;
|
||||
use rustc_data_structures::packed::Pu128;
|
||||
use rustc_errors::{Applicability, Diag};
|
||||
@@ -17,10 +17,10 @@
|
||||
/// Do we need to suggest turbofish when suggesting a replacement method?
|
||||
/// Changing `fold` to `sum` needs it sometimes when the return type can't be
|
||||
/// inferred. This checks for some common cases where it can be safely omitted
|
||||
fn needs_turbofish<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) -> bool {
|
||||
let use_cx = expr_use_ctxt(cx, expr);
|
||||
if use_cx.same_ctxt
|
||||
&& let use_node = use_cx.use_node(cx)
|
||||
fn needs_turbofish<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) -> bool {
|
||||
let use_site = get_expr_use_site(cx.tcx, cx.typeck_results(), expr.span.ctxt(), expr);
|
||||
if use_site.same_ctxt
|
||||
&& let use_node = use_site.use_node(cx)
|
||||
&& let Some(ty) = use_node.defined_ty(cx)
|
||||
{
|
||||
// some common cases where turbofish isn't needed:
|
||||
@@ -209,7 +209,7 @@ fn check_fold_with_method(
|
||||
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
expr: &hir::Expr<'tcx>,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
init: &hir::Expr<'_>,
|
||||
acc: &hir::Expr<'_>,
|
||||
fold_span: Span,
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::{implements_trait, is_copy};
|
||||
use clippy_utils::{DefinedTy, ExprUseNode, expr_use_ctxt, peel_n_hir_expr_refs, sym};
|
||||
use clippy_utils::{DefinedTy, ExprUseNode, get_expr_use_site, peel_n_hir_expr_refs, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
@@ -17,6 +17,7 @@
|
||||
self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, ParamTy, ProjectionPredicate, Ty,
|
||||
};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::SyntaxContext;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::{Obligation, ObligationCause};
|
||||
use std::collections::VecDeque;
|
||||
@@ -82,10 +83,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
if matches!(expr.kind, ExprKind::AddrOf(..))
|
||||
&& !expr.span.from_expansion()
|
||||
&& let use_cx = expr_use_ctxt(cx, expr)
|
||||
&& use_cx.same_ctxt
|
||||
&& !use_cx.is_ty_unified
|
||||
&& let use_node = use_cx.use_node(cx)
|
||||
&& let use_site = get_expr_use_site(cx.tcx, cx.typeck_results(), SyntaxContext::root(), expr)
|
||||
&& use_site.same_ctxt
|
||||
&& !use_site.is_ty_unified
|
||||
&& let use_node = use_site.use_node(cx)
|
||||
&& let Some(DefinedTy::Mir { def_site_def_id: _, ty }) = use_node.defined_ty(cx)
|
||||
&& let ty::Param(param_ty) = *ty.skip_binder().kind()
|
||||
&& let Some((hir_id, fn_id, i)) = match use_node {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt, integer_const, is_zero_integer_const};
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{ExprUseNode, clip, expr_use_ctxt, peel_hir_expr_refs, unsext};
|
||||
use clippy_utils::{ExprUseNode, clip, get_expr_use_site, peel_hir_expr_refs, unsext};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind, Node, Path, QPath};
|
||||
@@ -250,7 +250,7 @@ fn span_ineffective_operation(
|
||||
}
|
||||
|
||||
fn is_expr_used_with_type_annotation<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
|
||||
match expr_use_ctxt(cx, expr).use_node(cx) {
|
||||
match get_expr_use_site(cx.tcx, cx.typeck_results(), expr.span.ctxt(), expr).use_node(cx) {
|
||||
ExprUseNode::LetStmt(letstmt) => letstmt.ty.is_some(),
|
||||
ExprUseNode::Return(_) => true,
|
||||
_ => false,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{expr_use_ctxt, fn_def_id, get_parent_expr, higher, is_in_const_context, is_integer_const, sym};
|
||||
use clippy_utils::{fn_def_id, get_expr_use_site, get_parent_expr, higher, is_in_const_context, is_integer_const, sym};
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_ast::ast::RangeLimits;
|
||||
use rustc_errors::Applicability;
|
||||
@@ -14,7 +14,7 @@
|
||||
use rustc_lint::{LateContext, LateLintPass, Lint};
|
||||
use rustc_middle::ty::{self, ClauseKind, GenericArgKind, PredicatePolarity, Ty};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{DesugaringKind, Span, Spanned};
|
||||
use rustc_span::{DesugaringKind, Span, Spanned, SyntaxContext};
|
||||
use std::cmp::Ordering;
|
||||
|
||||
declare_clippy_lint! {
|
||||
@@ -355,18 +355,19 @@ fn check_range_bounds<'a>(cx: &'a LateContext<'_>, ex: &'a Expr<'_>) -> Option<R
|
||||
/// check that the obligations are still satisfied after switching the range type.
|
||||
fn can_switch_ranges<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
expr: &'tcx Expr<'_>,
|
||||
original: RangeLimits,
|
||||
inner_ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let use_ctxt = expr_use_ctxt(cx, expr);
|
||||
let (Node::Expr(parent_expr), false) = (use_ctxt.node, use_ctxt.is_ty_unified) else {
|
||||
let use_site = get_expr_use_site(cx.tcx, cx.typeck_results(), ctxt, expr);
|
||||
let (Node::Expr(parent_expr), false) = (use_site.node, use_site.is_ty_unified) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// Check if `expr` is the argument of a compiler-generated `IntoIter::into_iter(expr)`
|
||||
if let ExprKind::Call(func, [arg]) = parent_expr.kind
|
||||
&& arg.hir_id == use_ctxt.child_id
|
||||
&& arg.hir_id == use_site.child_id
|
||||
&& let ExprKind::Path(qpath) = func.kind
|
||||
&& cx.tcx.qpath_is_lang_item(qpath, LangItem::IntoIterIntoIter)
|
||||
&& parent_expr.span.is_desugaring(DesugaringKind::ForLoop)
|
||||
@@ -377,7 +378,7 @@ fn can_switch_ranges<'tcx>(
|
||||
// Check if `expr` is used as the receiver of a method of the `Iterator`, `IntoIterator`,
|
||||
// or `RangeBounds` traits.
|
||||
if let ExprKind::MethodCall(_, receiver, _, _) = parent_expr.kind
|
||||
&& receiver.hir_id == use_ctxt.child_id
|
||||
&& receiver.hir_id == use_site.child_id
|
||||
&& let Some(method_did) = cx.typeck_results().type_dependent_def_id(parent_expr.hir_id)
|
||||
&& let Some(trait_did) = cx.tcx.trait_of_assoc(method_did)
|
||||
&& matches!(
|
||||
@@ -392,7 +393,7 @@ fn can_switch_ranges<'tcx>(
|
||||
// or `RangeBounds` trait.
|
||||
if let ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) = parent_expr.kind
|
||||
&& let Some(id) = fn_def_id(cx, parent_expr)
|
||||
&& let Some(arg_idx) = args.iter().position(|e| e.hir_id == use_ctxt.child_id)
|
||||
&& let Some(arg_idx) = args.iter().position(|e| e.hir_id == use_site.child_id)
|
||||
{
|
||||
let input_idx = if matches!(parent_expr.kind, ExprKind::MethodCall(..)) {
|
||||
arg_idx + 1
|
||||
@@ -512,7 +513,7 @@ fn check_range_switch<'tcx>(
|
||||
&& span.can_be_used_for_suggestions()
|
||||
&& limits == kind
|
||||
&& let Some(y) = predicate(cx, end)
|
||||
&& can_switch_ranges(cx, expr, kind, cx.typeck_results().expr_ty(y))
|
||||
&& can_switch_ranges(cx, span.ctxt(), expr, kind, cx.typeck_results().expr_ty(y))
|
||||
{
|
||||
span_lint_and_then(cx, lint, span, msg, |diag| {
|
||||
let mut app = Applicability::MachineApplicable;
|
||||
|
||||
+215
-146
@@ -1,6 +1,5 @@
|
||||
#![feature(box_patterns)]
|
||||
#![feature(macro_metavar_expr)]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(unwrap_infallible)]
|
||||
#![recursion_limit = "512"]
|
||||
@@ -94,11 +93,12 @@
|
||||
use rustc_hir::hir_id::{HirIdMap, HirIdSet};
|
||||
use rustc_hir::intravisit::{Visitor, walk_expr};
|
||||
use rustc_hir::{
|
||||
self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring,
|
||||
CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs,
|
||||
HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId,
|
||||
OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
|
||||
TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr,
|
||||
self as hir, AnonConst, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, CRATE_HIR_ID, Closure, ConstArg,
|
||||
ConstArgKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind,
|
||||
FieldDef, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem,
|
||||
LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path,
|
||||
PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp, Variant, def,
|
||||
find_attr,
|
||||
};
|
||||
use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
|
||||
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
||||
@@ -110,12 +110,12 @@
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{
|
||||
self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt,
|
||||
TypeFlags, TypeVisitableExt, UintTy, UpvarCapture,
|
||||
TypeFlags, TypeVisitableExt, TypeckResults, UintTy, UpvarCapture,
|
||||
};
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::symbol::{Ident, Symbol, kw};
|
||||
use rustc_span::{InnerSpan, Span};
|
||||
use rustc_span::{InnerSpan, Span, SyntaxContext};
|
||||
use source::{SpanRangeExt, walk_span_to_context};
|
||||
use visitors::{Visitable, for_each_unconsumed_temporary};
|
||||
|
||||
@@ -825,11 +825,10 @@ fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
|
||||
ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(_), .. }))
|
||||
));
|
||||
|
||||
let mut child_id = e.hir_id;
|
||||
let mut capture = CaptureKind::Value;
|
||||
let mut capture_expr_ty = e;
|
||||
|
||||
for (parent_id, parent) in cx.tcx.hir_parent_iter(e.hir_id) {
|
||||
for (parent, child_id) in hir_parent_with_src_iter(cx.tcx, e.hir_id) {
|
||||
if let [
|
||||
Adjustment {
|
||||
kind: Adjust::Deref(_) | Adjust::Borrow(AutoBorrow::Ref(..)),
|
||||
@@ -885,8 +884,6 @@ fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
|
||||
},
|
||||
_ => break,
|
||||
}
|
||||
|
||||
child_id = parent_id;
|
||||
}
|
||||
|
||||
if capture == CaptureKind::Value && is_copy(cx, cx.typeck_results().expr_ty(capture_expr_ty)) {
|
||||
@@ -1282,38 +1279,28 @@ pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
|
||||
/// Checks if the given expression is a part of `let else`
|
||||
/// returns `true` for both the `init` and the `else` part
|
||||
pub fn is_inside_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
|
||||
let mut child_id = expr.hir_id;
|
||||
for (parent_id, node) in tcx.hir_parent_iter(child_id) {
|
||||
if let Node::LetStmt(LetStmt {
|
||||
init: Some(init),
|
||||
els: Some(els),
|
||||
..
|
||||
}) = node
|
||||
&& (init.hir_id == child_id || els.hir_id == child_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
child_id = parent_id;
|
||||
}
|
||||
|
||||
false
|
||||
hir_parent_with_src_iter(tcx, expr.hir_id).any(|(node, child_id)| {
|
||||
matches!(
|
||||
node,
|
||||
Node::LetStmt(LetStmt {
|
||||
init: Some(init),
|
||||
els: Some(els),
|
||||
..
|
||||
})
|
||||
if init.hir_id == child_id || els.hir_id == child_id
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks if the given expression is the else clause of a `let else` expression
|
||||
pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
|
||||
let mut child_id = expr.hir_id;
|
||||
for (parent_id, node) in tcx.hir_parent_iter(child_id) {
|
||||
if let Node::LetStmt(LetStmt { els: Some(els), .. }) = node
|
||||
&& els.hir_id == child_id
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
child_id = parent_id;
|
||||
}
|
||||
|
||||
false
|
||||
hir_parent_with_src_iter(tcx, expr.hir_id).any(|(node, child_id)| {
|
||||
matches!(
|
||||
node,
|
||||
Node::LetStmt(LetStmt { els: Some(els), .. })
|
||||
if els.hir_id == child_id
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Checks whether the given `Expr` is a range equivalent to a `RangeFull`.
|
||||
@@ -2033,22 +2020,20 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool
|
||||
/// Gets the node where an expression is either used, or it's type is unified with another branch.
|
||||
/// Returns both the node and the `HirId` of the closest child node.
|
||||
pub fn get_expr_use_or_unification_node<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<(Node<'tcx>, HirId)> {
|
||||
let mut child_id = expr.hir_id;
|
||||
let mut iter = tcx.hir_parent_iter(child_id);
|
||||
loop {
|
||||
match iter.next() {
|
||||
None => break None,
|
||||
Some((id, Node::Block(_))) => child_id = id,
|
||||
Some((id, Node::Arm(arm))) if arm.body.hir_id == child_id => child_id = id,
|
||||
Some((_, Node::Expr(expr))) => match expr.kind {
|
||||
ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => child_id = expr.hir_id,
|
||||
ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = expr.hir_id,
|
||||
ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => break None,
|
||||
_ => break Some((Node::Expr(expr), child_id)),
|
||||
for (node, child_id) in hir_parent_with_src_iter(tcx, expr.hir_id) {
|
||||
match node {
|
||||
Node::Block(_) => {},
|
||||
Node::Arm(arm) if arm.body.hir_id == child_id => {},
|
||||
Node::Expr(expr) => match expr.kind {
|
||||
ExprKind::Block(..) | ExprKind::DropTemps(_) => {},
|
||||
ExprKind::Match(_, [arm], _) if arm.hir_id == child_id => {},
|
||||
ExprKind::If(_, then_expr, None) if then_expr.hir_id == child_id => return None,
|
||||
_ => return Some((Node::Expr(expr), child_id)),
|
||||
},
|
||||
Some((_, node)) => break Some((node, child_id)),
|
||||
node => return Some((node, child_id)),
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Checks if the result of an expression is used, or it's type is unified with another branch.
|
||||
@@ -2493,60 +2478,12 @@ pub fn is_in_test(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
|
||||
pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
||||
find_attr!(tcx, def_id, CfgTrace(..))
|
||||
|| find_attr!(
|
||||
tcx.hir_parent_iter(tcx.local_def_id_to_hir_id(def_id))
|
||||
.flat_map(|(parent_id, _)| tcx.hir_attrs(parent_id)),
|
||||
tcx.hir_parent_id_iter(tcx.local_def_id_to_hir_id(def_id))
|
||||
.flat_map(|parent_id| tcx.hir_attrs(parent_id)),
|
||||
CfgTrace(..)
|
||||
)
|
||||
}
|
||||
|
||||
/// Walks up the HIR tree from the given expression in an attempt to find where the value is
|
||||
/// consumed.
|
||||
///
|
||||
/// Termination has three conditions:
|
||||
/// - The given function returns `Break`. This function will return the value.
|
||||
/// - The consuming node is found. This function will return `Continue(use_node, child_id)`.
|
||||
/// - No further parent nodes are found. This will trigger a debug assert or return `None`.
|
||||
///
|
||||
/// This allows walking through `if`, `match`, `break`, and block expressions to find where the
|
||||
/// value produced by the expression is consumed.
|
||||
pub fn walk_to_expr_usage<'tcx, T>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &Expr<'tcx>,
|
||||
mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
|
||||
) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
|
||||
let mut iter = cx.tcx.hir_parent_iter(e.hir_id);
|
||||
let mut child_id = e.hir_id;
|
||||
|
||||
while let Some((parent_id, parent)) = iter.next() {
|
||||
if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
|
||||
return Some(ControlFlow::Break(x));
|
||||
}
|
||||
let parent_expr = match parent {
|
||||
Node::Expr(e) => e,
|
||||
Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
|
||||
child_id = parent_id;
|
||||
continue;
|
||||
},
|
||||
Node::Arm(a) if a.body.hir_id == child_id => {
|
||||
child_id = parent_id;
|
||||
continue;
|
||||
},
|
||||
_ => return Some(ControlFlow::Continue((parent, child_id))),
|
||||
};
|
||||
match parent_expr.kind {
|
||||
ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
|
||||
ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
|
||||
child_id = id;
|
||||
iter = cx.tcx.hir_parent_iter(id);
|
||||
},
|
||||
ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
|
||||
_ => return Some(ControlFlow::Continue((parent, child_id))),
|
||||
}
|
||||
}
|
||||
debug_assert!(false, "no parent node found for `{child_id:?}`");
|
||||
None
|
||||
}
|
||||
|
||||
/// A type definition as it would be viewed from within a function.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum DefinedTy<'tcx> {
|
||||
@@ -2565,11 +2502,11 @@ pub enum DefinedTy<'tcx> {
|
||||
},
|
||||
}
|
||||
|
||||
/// The context an expressions value is used in.
|
||||
pub struct ExprUseCtxt<'tcx> {
|
||||
/// The location that recives the value of an expression.
|
||||
pub struct ExprUseSite<'tcx> {
|
||||
/// The parent node which consumes the value.
|
||||
pub node: Node<'tcx>,
|
||||
/// The child id of the node the value came from.
|
||||
/// The ID of the immediate child of the use node.
|
||||
pub child_id: HirId,
|
||||
/// Any adjustments applied to the type.
|
||||
pub adjustments: &'tcx [Adjustment<'tcx>],
|
||||
@@ -2580,7 +2517,7 @@ pub struct ExprUseCtxt<'tcx> {
|
||||
/// Whether the use site has the same `SyntaxContext` as the value.
|
||||
pub same_ctxt: bool,
|
||||
}
|
||||
impl<'tcx> ExprUseCtxt<'tcx> {
|
||||
impl<'tcx> ExprUseSite<'tcx> {
|
||||
pub fn use_node(&self, cx: &LateContext<'tcx>) -> ExprUseNode<'tcx> {
|
||||
match self.node {
|
||||
Node::LetStmt(l) => ExprUseNode::LetStmt(l),
|
||||
@@ -2746,54 +2683,178 @@ pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the context an expression's value is used in.
|
||||
pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtxt<'tcx> {
|
||||
let mut adjustments = [].as_slice();
|
||||
struct ReplacingFilterMap<I, F>(I, F);
|
||||
impl<I, F, U> Iterator for ReplacingFilterMap<I, F>
|
||||
where
|
||||
I: Iterator,
|
||||
F: FnMut(&mut I, I::Item) -> Option<U>,
|
||||
{
|
||||
type Item = U;
|
||||
fn next(&mut self) -> Option<U> {
|
||||
while let Some(x) = self.0.next() {
|
||||
if let Some(x) = (self.1)(&mut self.0, x) {
|
||||
return Some(x);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator which walks successive value using parent nodes skipping any node
|
||||
/// which simply moves a value.
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub fn expr_use_sites<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typeck: &'tcx TypeckResults<'tcx>,
|
||||
mut ctxt: SyntaxContext,
|
||||
e: &'tcx Expr<'tcx>,
|
||||
) -> impl Iterator<Item = ExprUseSite<'tcx>> {
|
||||
let mut adjustments: &[_] = typeck.expr_adjustments(e);
|
||||
let mut is_ty_unified = false;
|
||||
let mut moved_before_use = false;
|
||||
let mut same_ctxt = true;
|
||||
let ctxt = e.span.ctxt();
|
||||
let node = walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| -> ControlFlow<!> {
|
||||
if adjustments.is_empty()
|
||||
&& let Node::Expr(e) = cx.tcx.hir_node(child_id)
|
||||
{
|
||||
adjustments = cx.typeck_results().expr_adjustments(e);
|
||||
}
|
||||
same_ctxt &= cx.tcx.hir_span(parent_id).ctxt() == ctxt;
|
||||
if let Node::Expr(e) = parent {
|
||||
match e.kind {
|
||||
ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
|
||||
is_ty_unified = true;
|
||||
moved_before_use = true;
|
||||
ReplacingFilterMap(
|
||||
hir_parent_with_src_iter(tcx, e.hir_id),
|
||||
move |iter: &mut _, (parent, child_id)| {
|
||||
let parent_ctxt;
|
||||
let mut parent_adjustments: &[_] = &[];
|
||||
match parent {
|
||||
Node::Expr(parent_expr) => {
|
||||
parent_ctxt = parent_expr.span.ctxt();
|
||||
same_ctxt &= parent_ctxt == ctxt;
|
||||
parent_adjustments = typeck.expr_adjustments(parent_expr);
|
||||
match parent_expr.kind {
|
||||
ExprKind::Match(scrutinee, arms, _) if scrutinee.hir_id != child_id => {
|
||||
is_ty_unified |= arms.len() != 1;
|
||||
moved_before_use = true;
|
||||
if adjustments.is_empty() {
|
||||
adjustments = parent_adjustments;
|
||||
}
|
||||
return None;
|
||||
},
|
||||
ExprKind::If(cond, _, else_) if cond.hir_id != child_id => {
|
||||
is_ty_unified |= else_.is_some();
|
||||
moved_before_use = true;
|
||||
if adjustments.is_empty() {
|
||||
adjustments = parent_adjustments;
|
||||
}
|
||||
return None;
|
||||
},
|
||||
ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
|
||||
is_ty_unified = true;
|
||||
moved_before_use = true;
|
||||
*iter = hir_parent_with_src_iter(tcx, id);
|
||||
if adjustments.is_empty() {
|
||||
adjustments = parent_adjustments;
|
||||
}
|
||||
return None;
|
||||
},
|
||||
ExprKind::Block(b, _) => {
|
||||
is_ty_unified |= b.targeted_by_break;
|
||||
moved_before_use = true;
|
||||
if adjustments.is_empty() {
|
||||
adjustments = parent_adjustments;
|
||||
}
|
||||
return None;
|
||||
},
|
||||
ExprKind::DropTemps(_) | ExprKind::Type(..) => {
|
||||
if adjustments.is_empty() {
|
||||
adjustments = parent_adjustments;
|
||||
}
|
||||
return None;
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
},
|
||||
ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
|
||||
is_ty_unified = true;
|
||||
moved_before_use = true;
|
||||
Node::Arm(arm) => {
|
||||
parent_ctxt = arm.span.ctxt();
|
||||
same_ctxt &= parent_ctxt == ctxt;
|
||||
if arm.body.hir_id == child_id {
|
||||
return None;
|
||||
}
|
||||
},
|
||||
Node::Block(b) => {
|
||||
same_ctxt &= b.span.ctxt() == ctxt;
|
||||
return None;
|
||||
},
|
||||
Node::ConstBlock(_) => parent_ctxt = ctxt,
|
||||
Node::ExprField(&ExprField { span, .. }) => {
|
||||
parent_ctxt = span.ctxt();
|
||||
same_ctxt &= parent_ctxt == ctxt;
|
||||
},
|
||||
Node::AnonConst(&AnonConst { span, .. })
|
||||
| Node::ConstArg(&ConstArg { span, .. })
|
||||
| Node::Field(&FieldDef { span, .. })
|
||||
| Node::ImplItem(&ImplItem { span, .. })
|
||||
| Node::Item(&Item { span, .. })
|
||||
| Node::LetStmt(&LetStmt { span, .. })
|
||||
| Node::Stmt(&Stmt { span, .. })
|
||||
| Node::TraitItem(&TraitItem { span, .. })
|
||||
| Node::Variant(&Variant { span, .. }) => {
|
||||
parent_ctxt = span.ctxt();
|
||||
same_ctxt &= parent_ctxt == ctxt;
|
||||
*iter = hir_parent_with_src_iter(tcx, CRATE_HIR_ID);
|
||||
},
|
||||
Node::AssocItemConstraint(_)
|
||||
| Node::ConstArgExprField(_)
|
||||
| Node::Crate(_)
|
||||
| Node::Ctor(_)
|
||||
| Node::Err(_)
|
||||
| Node::ForeignItem(_)
|
||||
| Node::GenericParam(_)
|
||||
| Node::Infer(_)
|
||||
| Node::Lifetime(_)
|
||||
| Node::OpaqueTy(_)
|
||||
| Node::Param(_)
|
||||
| Node::Pat(_)
|
||||
| Node::PatExpr(_)
|
||||
| Node::PatField(_)
|
||||
| Node::PathSegment(_)
|
||||
| Node::PreciseCapturingNonLifetimeArg(_)
|
||||
| Node::Synthetic
|
||||
| Node::TraitRef(_)
|
||||
| Node::Ty(_)
|
||||
| Node::TyPat(_)
|
||||
| Node::WherePredicate(_) => {
|
||||
// This shouldn't be possible to hit; the inner iterator should have
|
||||
// been moved to the end before we hit any of these nodes.
|
||||
debug_assert!(false, "found {parent:?} which is after the final use node");
|
||||
return None;
|
||||
},
|
||||
ExprKind::Block(..) => moved_before_use = true,
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
});
|
||||
match node {
|
||||
Some(ControlFlow::Continue((node, child_id))) => ExprUseCtxt {
|
||||
node,
|
||||
child_id,
|
||||
adjustments,
|
||||
is_ty_unified,
|
||||
moved_before_use,
|
||||
same_ctxt,
|
||||
|
||||
ctxt = parent_ctxt;
|
||||
Some(ExprUseSite {
|
||||
node: parent,
|
||||
child_id,
|
||||
adjustments: mem::replace(&mut adjustments, parent_adjustments),
|
||||
is_ty_unified: mem::replace(&mut is_ty_unified, false),
|
||||
moved_before_use: mem::replace(&mut moved_before_use, false),
|
||||
same_ctxt: mem::replace(&mut same_ctxt, true),
|
||||
})
|
||||
},
|
||||
None => ExprUseCtxt {
|
||||
node: Node::Crate(cx.tcx.hir_root_module()),
|
||||
child_id: HirId::INVALID,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn get_expr_use_site<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
typeck: &'tcx TypeckResults<'tcx>,
|
||||
ctxt: SyntaxContext,
|
||||
e: &'tcx Expr<'tcx>,
|
||||
) -> ExprUseSite<'tcx> {
|
||||
// The value in `unwrap_or` doesn't actually matter; an expression always
|
||||
// has a use site.
|
||||
expr_use_sites(tcx, typeck, ctxt, e).next().unwrap_or_else(|| {
|
||||
debug_assert!(false, "failed to find a use site for expr {e:?}");
|
||||
ExprUseSite {
|
||||
node: Node::Synthetic, // The crate root would also work.
|
||||
child_id: CRATE_HIR_ID,
|
||||
adjustments: &[],
|
||||
is_ty_unified: true,
|
||||
moved_before_use: true,
|
||||
is_ty_unified: false,
|
||||
moved_before_use: false,
|
||||
same_ctxt: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Tokenizes the input while keeping the text associated with each token.
|
||||
@@ -3628,3 +3689,11 @@ pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
|
||||
pub fn can_use_if_let_chains(cx: &LateContext<'_>, msrv: Msrv) -> bool {
|
||||
cx.tcx.sess.edition().at_least_rust_2024() && msrv.meets(cx, msrvs::LET_CHAINS)
|
||||
}
|
||||
|
||||
/// Returns an iterator over successive parent nodes paired with the ID of the node which
|
||||
/// immediatly preceeded them.
|
||||
#[inline]
|
||||
pub fn hir_parent_with_src_iter(tcx: TyCtxt<'_>, mut id: HirId) -> impl Iterator<Item = (Node<'_>, HirId)> {
|
||||
tcx.hir_parent_id_iter(id)
|
||||
.map(move |parent| (tcx.hir_node(parent), mem::replace(&mut id, parent)))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user