Merge commit 'a109190d70' into clippy-subtree-update

This commit is contained in:
Philipp Krones 2024-10-18 13:44:06 +02:00
parent c512a221c2
commit fea5e77da1
179 changed files with 4032 additions and 1752 deletions

View file

@ -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) {

View file

@ -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);
}
}
},
}

View file

@ -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]

View file

@ -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()
}
}