mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-15 09:27:25 +00:00
Auto merge of #11166 - Jarcho:expr_use, r=Centri3
Refactor some of `dereference.rs` to util functions I've seen a few lints that need to be able to tell if changing the type of an expression would be a vaild suggestion. This extracts part of how that's done from `explicit_auto_deref`. changelog: None
This commit is contained in:
commit
a4e64ff375
2 changed files with 673 additions and 641 deletions
File diff suppressed because it is too large
Load diff
|
@ -89,15 +89,14 @@ use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
|
|||
use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
|
||||
use rustc_hir::{
|
||||
self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Destination, Expr,
|
||||
ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, ItemKind, LangItem, Local,
|
||||
MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
|
||||
TraitItem, TraitItemRef, TraitRef, TyKind, UnOp,
|
||||
ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item,
|
||||
ItemKind, LangItem, Local, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy,
|
||||
QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp,
|
||||
};
|
||||
use rustc_lexer::{tokenize, TokenKind};
|
||||
use rustc_lint::{LateContext, Level, Lint, LintContext};
|
||||
use rustc_middle::hir::place::PlaceBase;
|
||||
use rustc_middle::mir::ConstantKind;
|
||||
use rustc_middle::ty as rustc_ty;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
|
||||
use rustc_middle::ty::binding::BindingMode;
|
||||
use rustc_middle::ty::fast_reject::SimplifiedType::{
|
||||
|
@ -106,7 +105,8 @@ use rustc_middle::ty::fast_reject::SimplifiedType::{
|
|||
};
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{
|
||||
BorrowKind, ClosureKind, FloatTy, IntTy, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, UintTy, UpvarCapture,
|
||||
self as rustc_ty, Binder, BorrowKind, ClosureKind, FloatTy, IntTy, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeAndMut,
|
||||
TypeVisitableExt, UintTy, UpvarCapture,
|
||||
};
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
use rustc_span::source_map::SourceMap;
|
||||
|
@ -116,7 +116,10 @@ use rustc_target::abi::Integer;
|
|||
|
||||
use crate::consts::{constant, miri_to_const, Constant};
|
||||
use crate::higher::Range;
|
||||
use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
|
||||
use crate::ty::{
|
||||
adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type,
|
||||
ty_is_fn_once_param,
|
||||
};
|
||||
use crate::visitors::for_each_expr;
|
||||
|
||||
use rustc_middle::hir::nested_filter;
|
||||
|
@ -2502,6 +2505,261 @@ pub fn walk_to_expr_usage<'tcx, T>(
|
|||
None
|
||||
}
|
||||
|
||||
/// A type definition as it would be viewed from within a function.
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum DefinedTy<'tcx> {
|
||||
// Used for locals and closures defined within the function.
|
||||
Hir(&'tcx hir::Ty<'tcx>),
|
||||
/// Used for function signatures, and constant and static values. This includes the `ParamEnv`
|
||||
/// from the definition site.
|
||||
Mir(ParamEnvAnd<'tcx, Binder<'tcx, Ty<'tcx>>>),
|
||||
}
|
||||
|
||||
/// The context an expressions value is used in.
|
||||
pub struct ExprUseCtxt<'tcx> {
|
||||
/// The parent node which consumes the value.
|
||||
pub node: ExprUseNode<'tcx>,
|
||||
/// Any adjustments applied to the type.
|
||||
pub adjustments: &'tcx [Adjustment<'tcx>],
|
||||
/// Whether or not the type must unify with another code path.
|
||||
pub is_ty_unified: bool,
|
||||
/// Whether or not the value will be moved before it's used.
|
||||
pub moved_before_use: bool,
|
||||
}
|
||||
|
||||
/// The node which consumes a value.
|
||||
pub enum ExprUseNode<'tcx> {
|
||||
/// Assignment to, or initializer for, a local
|
||||
Local(&'tcx Local<'tcx>),
|
||||
/// Initializer for a const or static item.
|
||||
ConstStatic(OwnerId),
|
||||
/// Implicit or explicit return from a function.
|
||||
Return(OwnerId),
|
||||
/// Initialization of a struct field.
|
||||
Field(&'tcx ExprField<'tcx>),
|
||||
/// An argument to a function.
|
||||
FnArg(&'tcx Expr<'tcx>, usize),
|
||||
/// An argument to a method.
|
||||
MethodArg(HirId, Option<&'tcx GenericArgs<'tcx>>, usize),
|
||||
/// The callee of a function call.
|
||||
Callee,
|
||||
/// Access of a field.
|
||||
FieldAccess(Ident),
|
||||
}
|
||||
impl<'tcx> ExprUseNode<'tcx> {
|
||||
/// Checks if the value is returned from the function.
|
||||
pub fn is_return(&self) -> bool {
|
||||
matches!(self, Self::Return(_))
|
||||
}
|
||||
|
||||
/// Checks if the value is used as a method call receiver.
|
||||
pub fn is_recv(&self) -> bool {
|
||||
matches!(self, Self::MethodArg(_, _, 0))
|
||||
}
|
||||
|
||||
/// Gets the needed type as it's defined without any type inference.
|
||||
pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
|
||||
match *self {
|
||||
Self::Local(Local { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
|
||||
Self::ConstStatic(id) => Some(DefinedTy::Mir(
|
||||
cx.param_env.and(Binder::dummy(cx.tcx.type_of(id).subst_identity())),
|
||||
)),
|
||||
Self::Return(id) => {
|
||||
let hir_id = cx.tcx.hir().local_def_id_to_hir_id(id.def_id);
|
||||
if let Some(Node::Expr(Expr {
|
||||
kind: ExprKind::Closure(c),
|
||||
..
|
||||
})) = cx.tcx.hir().find(hir_id)
|
||||
{
|
||||
match c.fn_decl.output {
|
||||
FnRetTy::DefaultReturn(_) => None,
|
||||
FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
|
||||
}
|
||||
} else {
|
||||
Some(DefinedTy::Mir(
|
||||
cx.param_env.and(cx.tcx.fn_sig(id).subst_identity().output()),
|
||||
))
|
||||
}
|
||||
},
|
||||
Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
|
||||
Some(Expr {
|
||||
hir_id,
|
||||
kind: ExprKind::Struct(path, ..),
|
||||
..
|
||||
}) => adt_and_variant_of_res(cx, cx.qpath_res(path, *hir_id))
|
||||
.and_then(|(adt, variant)| {
|
||||
variant
|
||||
.fields
|
||||
.iter()
|
||||
.find(|f| f.name == field.ident.name)
|
||||
.map(|f| (adt, f))
|
||||
})
|
||||
.map(|(adt, field_def)| {
|
||||
DefinedTy::Mir(
|
||||
cx.tcx
|
||||
.param_env(adt.did())
|
||||
.and(Binder::dummy(cx.tcx.type_of(field_def.did).subst_identity())),
|
||||
)
|
||||
}),
|
||||
_ => None,
|
||||
},
|
||||
Self::FnArg(callee, i) => {
|
||||
let sig = expr_sig(cx, callee)?;
|
||||
let (hir_ty, ty) = sig.input_with_hir(i)?;
|
||||
Some(match hir_ty {
|
||||
Some(hir_ty) => DefinedTy::Hir(hir_ty),
|
||||
None => DefinedTy::Mir(
|
||||
sig.predicates_id()
|
||||
.map_or(ParamEnv::empty(), |id| cx.tcx.param_env(id))
|
||||
.and(ty),
|
||||
),
|
||||
})
|
||||
},
|
||||
Self::MethodArg(id, _, i) => {
|
||||
let id = cx.typeck_results().type_dependent_def_id(id)?;
|
||||
let sig = cx.tcx.fn_sig(id).skip_binder();
|
||||
Some(DefinedTy::Mir(cx.tcx.param_env(id).and(sig.input(i))))
|
||||
},
|
||||
Self::Local(_) | Self::FieldAccess(..) | Self::Callee => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the context an expression's value is used in.
|
||||
#[expect(clippy::too_many_lines)]
|
||||
pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> Option<ExprUseCtxt<'tcx>> {
|
||||
let mut adjustments = [].as_slice();
|
||||
let mut is_ty_unified = false;
|
||||
let mut moved_before_use = false;
|
||||
let ctxt = e.span.ctxt();
|
||||
walk_to_expr_usage(cx, e, &mut |parent, child_id| {
|
||||
// LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
|
||||
if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) {
|
||||
adjustments = cx.typeck_results().expr_adjustments(e);
|
||||
}
|
||||
match parent {
|
||||
Node::Local(l) if l.span.ctxt() == ctxt => Some(ExprUseCtxt {
|
||||
node: ExprUseNode::Local(l),
|
||||
adjustments,
|
||||
is_ty_unified,
|
||||
moved_before_use,
|
||||
}),
|
||||
Node::Item(&Item {
|
||||
kind: ItemKind::Static(..) | ItemKind::Const(..),
|
||||
owner_id,
|
||||
span,
|
||||
..
|
||||
})
|
||||
| Node::TraitItem(&TraitItem {
|
||||
kind: TraitItemKind::Const(..),
|
||||
owner_id,
|
||||
span,
|
||||
..
|
||||
})
|
||||
| Node::ImplItem(&ImplItem {
|
||||
kind: ImplItemKind::Const(..),
|
||||
owner_id,
|
||||
span,
|
||||
..
|
||||
}) if span.ctxt() == ctxt => Some(ExprUseCtxt {
|
||||
node: ExprUseNode::ConstStatic(owner_id),
|
||||
adjustments,
|
||||
is_ty_unified,
|
||||
moved_before_use,
|
||||
}),
|
||||
|
||||
Node::Item(&Item {
|
||||
kind: ItemKind::Fn(..),
|
||||
owner_id,
|
||||
span,
|
||||
..
|
||||
})
|
||||
| Node::TraitItem(&TraitItem {
|
||||
kind: TraitItemKind::Fn(..),
|
||||
owner_id,
|
||||
span,
|
||||
..
|
||||
})
|
||||
| Node::ImplItem(&ImplItem {
|
||||
kind: ImplItemKind::Fn(..),
|
||||
owner_id,
|
||||
span,
|
||||
..
|
||||
}) if span.ctxt() == ctxt => Some(ExprUseCtxt {
|
||||
node: ExprUseNode::Return(owner_id),
|
||||
adjustments,
|
||||
is_ty_unified,
|
||||
moved_before_use,
|
||||
}),
|
||||
|
||||
Node::ExprField(field) if field.span.ctxt() == ctxt => Some(ExprUseCtxt {
|
||||
node: ExprUseNode::Field(field),
|
||||
adjustments,
|
||||
is_ty_unified,
|
||||
moved_before_use,
|
||||
}),
|
||||
|
||||
Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
|
||||
ExprKind::Ret(_) => Some(ExprUseCtxt {
|
||||
node: ExprUseNode::Return(OwnerId {
|
||||
def_id: cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()),
|
||||
}),
|
||||
adjustments,
|
||||
is_ty_unified,
|
||||
moved_before_use,
|
||||
}),
|
||||
ExprKind::Closure(closure) => Some(ExprUseCtxt {
|
||||
node: ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
|
||||
adjustments,
|
||||
is_ty_unified,
|
||||
moved_before_use,
|
||||
}),
|
||||
ExprKind::Call(func, args) => Some(ExprUseCtxt {
|
||||
node: match args.iter().position(|arg| arg.hir_id == child_id) {
|
||||
Some(i) => ExprUseNode::FnArg(func, i),
|
||||
None => ExprUseNode::Callee,
|
||||
},
|
||||
adjustments,
|
||||
is_ty_unified,
|
||||
moved_before_use,
|
||||
}),
|
||||
ExprKind::MethodCall(name, _, args, _) => Some(ExprUseCtxt {
|
||||
node: ExprUseNode::MethodArg(
|
||||
parent.hir_id,
|
||||
name.args,
|
||||
args.iter().position(|arg| arg.hir_id == child_id).map_or(0, |i| i + 1),
|
||||
),
|
||||
adjustments,
|
||||
is_ty_unified,
|
||||
moved_before_use,
|
||||
}),
|
||||
ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(ExprUseCtxt {
|
||||
node: ExprUseNode::FieldAccess(name),
|
||||
adjustments,
|
||||
is_ty_unified,
|
||||
moved_before_use,
|
||||
}),
|
||||
ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
|
||||
is_ty_unified = true;
|
||||
moved_before_use = true;
|
||||
None
|
||||
},
|
||||
ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
|
||||
is_ty_unified = true;
|
||||
moved_before_use = true;
|
||||
None
|
||||
},
|
||||
ExprKind::Block(..) => {
|
||||
moved_before_use = true;
|
||||
None
|
||||
},
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Tokenizes the input while keeping the text associated with each token.
|
||||
pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str)> {
|
||||
let mut pos = 0;
|
||||
|
|
Loading…
Reference in a new issue