Merge commit 'a109190d70' into clippy-subtree-update
This commit is contained in:
parent
c512a221c2
commit
fea5e77da1
179 changed files with 4032 additions and 1752 deletions
|
|
@ -484,10 +484,9 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
|
|||
}),
|
||||
ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
|
||||
ExprKind::Binary(op, left, right) => self.binop(op, left, right),
|
||||
ExprKind::Call(callee, args) => {
|
||||
ExprKind::Call(callee, []) => {
|
||||
// We only handle a few const functions for now.
|
||||
if args.is_empty()
|
||||
&& let ExprKind::Path(qpath) = &callee.kind
|
||||
if let ExprKind::Path(qpath) = &callee.kind
|
||||
&& let Some(did) = self.typeck.qpath_res(qpath, callee.hir_id).opt_def_id()
|
||||
{
|
||||
match self.tcx.get_diagnostic_name(did) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::tokenize_with_text;
|
|||
use rustc_ast::ast::InlineAsmTemplatePiece;
|
||||
use rustc_data_structures::fx::FxHasher;
|
||||
use rustc_hir::MatchSource::TryDesugar;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{
|
||||
ArrayLen, AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr,
|
||||
ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime,
|
||||
|
|
@ -17,11 +17,33 @@ use rustc_middle::ty::TypeckResults;
|
|||
use rustc_span::{BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext, sym};
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Range;
|
||||
use std::slice;
|
||||
|
||||
/// Callback that is called when two expressions are not equal in the sense of `SpanlessEq`, but
|
||||
/// other conditions would make them equal.
|
||||
type SpanlessEqCallback<'a> = dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a;
|
||||
|
||||
/// Determines how paths are hashed and compared for equality.
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub enum PathCheck {
|
||||
/// Paths must match exactly and are hashed by their exact HIR tree.
|
||||
///
|
||||
/// Thus, `std::iter::Iterator` and `Iterator` are not considered equal even though they refer
|
||||
/// to the same item.
|
||||
#[default]
|
||||
Exact,
|
||||
/// Paths are compared and hashed based on their resolution.
|
||||
///
|
||||
/// They can appear different in the HIR tree but are still considered equal
|
||||
/// and have equal hashes as long as they refer to the same item.
|
||||
///
|
||||
/// Note that this is currently only partially implemented specifically for paths that are
|
||||
/// resolved before type-checking, i.e. the final segment must have a non-error resolution.
|
||||
/// If a path with an error resolution is encountered, it falls back to the default exact
|
||||
/// matching behavior.
|
||||
Resolution,
|
||||
}
|
||||
|
||||
/// Type used to check whether two ast are the same. This is different from the
|
||||
/// operator `==` on ast types as this operator would compare true equality with
|
||||
/// ID and span.
|
||||
|
|
@ -33,6 +55,7 @@ pub struct SpanlessEq<'a, 'tcx> {
|
|||
maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>,
|
||||
allow_side_effects: bool,
|
||||
expr_fallback: Option<Box<SpanlessEqCallback<'a>>>,
|
||||
path_check: PathCheck,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
||||
|
|
@ -42,6 +65,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
maybe_typeck_results: cx.maybe_typeck_results().map(|x| (x, x)),
|
||||
allow_side_effects: true,
|
||||
expr_fallback: None,
|
||||
path_check: PathCheck::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -54,6 +78,16 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check paths by their resolution instead of exact equality. See [`PathCheck`] for more
|
||||
/// details.
|
||||
#[must_use]
|
||||
pub fn paths_by_resolution(self) -> Self {
|
||||
Self {
|
||||
path_check: PathCheck::Resolution,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn expr_fallback(self, expr_fallback: impl FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a) -> Self {
|
||||
Self {
|
||||
|
|
@ -498,7 +532,7 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
match (left.res, right.res) {
|
||||
(Res::Local(l), Res::Local(r)) => l == r || self.locals.get(&l) == Some(&r),
|
||||
(Res::Local(_), _) | (_, Res::Local(_)) => false,
|
||||
_ => over(left.segments, right.segments, |l, r| self.eq_path_segment(l, r)),
|
||||
_ => self.eq_path_segments(left.segments, right.segments),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -511,17 +545,39 @@ impl HirEqInterExpr<'_, '_, '_> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
|
||||
left.len() == right.len() && left.iter().zip(right).all(|(l, r)| self.eq_path_segment(l, r))
|
||||
pub fn eq_path_segments<'tcx>(
|
||||
&mut self,
|
||||
mut left: &'tcx [PathSegment<'tcx>],
|
||||
mut right: &'tcx [PathSegment<'tcx>],
|
||||
) -> bool {
|
||||
if let PathCheck::Resolution = self.inner.path_check
|
||||
&& let Some(left_seg) = generic_path_segments(left)
|
||||
&& let Some(right_seg) = generic_path_segments(right)
|
||||
{
|
||||
// If we compare by resolution, then only check the last segments that could possibly have generic
|
||||
// arguments
|
||||
left = left_seg;
|
||||
right = right_seg;
|
||||
}
|
||||
|
||||
over(left, right, |l, r| self.eq_path_segment(l, r))
|
||||
}
|
||||
|
||||
pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_>) -> bool {
|
||||
// The == of idents doesn't work with different contexts,
|
||||
// we have to be explicit about hygiene
|
||||
left.ident.name == right.ident.name
|
||||
&& both(left.args.as_ref(), right.args.as_ref(), |l, r| {
|
||||
self.eq_path_parameters(l, r)
|
||||
})
|
||||
if !self.eq_path_parameters(left.args(), right.args()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let PathCheck::Resolution = self.inner.path_check
|
||||
&& left.res != Res::Err
|
||||
&& right.res != Res::Err
|
||||
{
|
||||
left.res == right.res
|
||||
} else {
|
||||
// The == of idents doesn't work with different contexts,
|
||||
// we have to be explicit about hygiene
|
||||
left.ident.name == right.ident.name
|
||||
}
|
||||
}
|
||||
|
||||
pub fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
|
||||
|
|
@ -684,6 +740,21 @@ pub fn eq_expr_value(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>) ->
|
|||
SpanlessEq::new(cx).deny_side_effects().eq_expr(left, right)
|
||||
}
|
||||
|
||||
/// Returns the segments of a path that might have generic parameters.
|
||||
/// Usually just the last segment for free items, except for when the path resolves to an associated
|
||||
/// item, in which case it is the last two
|
||||
fn generic_path_segments<'tcx>(segments: &'tcx [PathSegment<'tcx>]) -> Option<&'tcx [PathSegment<'tcx>]> {
|
||||
match segments.last()?.res {
|
||||
Res::Def(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, _) => {
|
||||
// <Ty as module::Trait<T>>::assoc::<U>
|
||||
// ^^^^^^^^^^^^^^^^ ^^^^^^^^^^ segments: [module, Trait<T>, assoc<U>]
|
||||
Some(&segments[segments.len().checked_sub(2)?..])
|
||||
},
|
||||
Res::Err => None,
|
||||
_ => Some(slice::from_ref(segments.last()?)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Type used to hash an ast element. This is different from the `Hash` trait
|
||||
/// on ast types as this
|
||||
/// trait would consider IDs and spans.
|
||||
|
|
@ -694,6 +765,7 @@ pub struct SpanlessHash<'a, 'tcx> {
|
|||
cx: &'a LateContext<'tcx>,
|
||||
maybe_typeck_results: Option<&'tcx TypeckResults<'tcx>>,
|
||||
s: FxHasher,
|
||||
path_check: PathCheck,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
||||
|
|
@ -701,10 +773,21 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
Self {
|
||||
cx,
|
||||
maybe_typeck_results: cx.maybe_typeck_results(),
|
||||
path_check: PathCheck::default(),
|
||||
s: FxHasher::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check paths by their resolution instead of exact equality. See [`PathCheck`] for more
|
||||
/// details.
|
||||
#[must_use]
|
||||
pub fn paths_by_resolution(self) -> Self {
|
||||
Self {
|
||||
path_check: PathCheck::Resolution,
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish(self) -> u64 {
|
||||
self.s.finish()
|
||||
}
|
||||
|
|
@ -1042,9 +1125,19 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
|||
// even though the binding names are different and they have different `HirId`s.
|
||||
Res::Local(_) => 1_usize.hash(&mut self.s),
|
||||
_ => {
|
||||
for seg in path.segments {
|
||||
self.hash_name(seg.ident.name);
|
||||
self.hash_generic_args(seg.args().args);
|
||||
if let PathCheck::Resolution = self.path_check
|
||||
&& let [.., last] = path.segments
|
||||
&& let Some(segments) = generic_path_segments(path.segments)
|
||||
{
|
||||
for seg in segments {
|
||||
self.hash_generic_args(seg.args().args);
|
||||
}
|
||||
last.res.hash(&mut self.s);
|
||||
} else {
|
||||
for seg in path.segments {
|
||||
self.hash_name(seg.ident.name);
|
||||
self.hash_generic_args(seg.args().args);
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#![feature(f128)]
|
||||
#![feature(f16)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(macro_metavar_expr_concat)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustc_private)]
|
||||
|
|
@ -128,7 +129,6 @@ use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
|
|||
use crate::higher::Range;
|
||||
use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
|
||||
use crate::visitors::for_each_expr_without_closures;
|
||||
|
||||
use rustc_middle::hir::nested_filter;
|
||||
|
||||
#[macro_export]
|
||||
|
|
|
|||
|
|
@ -1168,7 +1168,7 @@ pub fn make_normalized_projection<'tcx>(
|
|||
pub struct InteriorMut<'tcx> {
|
||||
ignored_def_ids: FxHashSet<DefId>,
|
||||
ignore_pointers: bool,
|
||||
tys: FxHashMap<Ty<'tcx>, Option<bool>>,
|
||||
tys: FxHashMap<Ty<'tcx>, Option<&'tcx ty::List<Ty<'tcx>>>>,
|
||||
}
|
||||
|
||||
impl<'tcx> InteriorMut<'tcx> {
|
||||
|
|
@ -1194,25 +1194,24 @@ impl<'tcx> InteriorMut<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Check if given type has inner mutability such as [`std::cell::Cell`] or
|
||||
/// [`std::cell::RefCell`] etc.
|
||||
pub fn is_interior_mut_ty(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
/// Check if given type has interior mutability such as [`std::cell::Cell`] or
|
||||
/// [`std::cell::RefCell`] etc. and if it does, returns a chain of types that causes
|
||||
/// this type to be interior mutable
|
||||
pub fn interior_mut_ty_chain(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx ty::List<Ty<'tcx>>> {
|
||||
match self.tys.entry(ty) {
|
||||
Entry::Occupied(o) => return *o.get() == Some(true),
|
||||
Entry::Occupied(o) => return *o.get(),
|
||||
// Temporarily insert a `None` to break cycles
|
||||
Entry::Vacant(v) => v.insert(None),
|
||||
};
|
||||
|
||||
let interior_mut = match *ty.kind() {
|
||||
ty::RawPtr(inner_ty, _) if !self.ignore_pointers => self.is_interior_mut_ty(cx, inner_ty),
|
||||
ty::Ref(_, inner_ty, _) | ty::Slice(inner_ty) => self.is_interior_mut_ty(cx, inner_ty),
|
||||
ty::Array(inner_ty, size) => {
|
||||
size.try_eval_target_usize(cx.tcx, cx.param_env)
|
||||
.map_or(true, |u| u != 0)
|
||||
&& self.is_interior_mut_ty(cx, inner_ty)
|
||||
let chain = match *ty.kind() {
|
||||
ty::RawPtr(inner_ty, _) if !self.ignore_pointers => self.interior_mut_ty_chain(cx, inner_ty),
|
||||
ty::Ref(_, inner_ty, _) | ty::Slice(inner_ty) => self.interior_mut_ty_chain(cx, inner_ty),
|
||||
ty::Array(inner_ty, size) if size.try_eval_target_usize(cx.tcx, cx.param_env) != Some(0) => {
|
||||
self.interior_mut_ty_chain(cx, inner_ty)
|
||||
},
|
||||
ty::Tuple(fields) => fields.iter().any(|ty| self.is_interior_mut_ty(cx, ty)),
|
||||
ty::Adt(def, _) if def.is_unsafe_cell() => true,
|
||||
ty::Tuple(fields) => fields.iter().find_map(|ty| self.interior_mut_ty_chain(cx, ty)),
|
||||
ty::Adt(def, _) if def.is_unsafe_cell() => Some(ty::List::empty()),
|
||||
ty::Adt(def, args) => {
|
||||
let is_std_collection = matches!(
|
||||
cx.tcx.get_diagnostic_name(def.did()),
|
||||
|
|
@ -1231,19 +1230,28 @@ impl<'tcx> InteriorMut<'tcx> {
|
|||
|
||||
if is_std_collection || def.is_box() {
|
||||
// Include the types from std collections that are behind pointers internally
|
||||
args.types().any(|ty| self.is_interior_mut_ty(cx, ty))
|
||||
args.types().find_map(|ty| self.interior_mut_ty_chain(cx, ty))
|
||||
} else if self.ignored_def_ids.contains(&def.did()) || def.is_phantom_data() {
|
||||
false
|
||||
None
|
||||
} else {
|
||||
def.all_fields()
|
||||
.any(|f| self.is_interior_mut_ty(cx, f.ty(cx.tcx, args)))
|
||||
.find_map(|f| self.interior_mut_ty_chain(cx, f.ty(cx.tcx, args)))
|
||||
}
|
||||
},
|
||||
_ => false,
|
||||
_ => None,
|
||||
};
|
||||
|
||||
self.tys.insert(ty, Some(interior_mut));
|
||||
interior_mut
|
||||
chain.map(|chain| {
|
||||
let list = cx.tcx.mk_type_list_from_iter(chain.iter().chain([ty]));
|
||||
self.tys.insert(ty, Some(list));
|
||||
list
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if given type has interior mutability such as [`std::cell::Cell`] or
|
||||
/// [`std::cell::RefCell`] etc.
|
||||
pub fn is_interior_mut_ty(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
self.interior_mut_ty_chain(cx, ty).is_some()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue