summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_utils
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:36 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-30 18:31:36 +0000
commite02c5b5930c2c9ba3e5423fe12e2ef0155017297 (patch)
treefd60ebbbb5299e16e5fca8c773ddb74f764760db /src/tools/clippy/clippy_utils
parentAdding debian version 1.73.0+dfsg1-1. (diff)
downloadrustc-e02c5b5930c2c9ba3e5423fe12e2ef0155017297.tar.xz
rustc-e02c5b5930c2c9ba3e5423fe12e2ef0155017297.zip
Merging upstream version 1.74.1+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/tools/clippy/clippy_utils')
-rw-r--r--src/tools/clippy/clippy_utils/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs89
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs3
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs45
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs35
-rw-r--r--src/tools/clippy/clippy_utils/src/mir/mod.rs72
-rw-r--r--src/tools/clippy/clippy_utils/src/msrvs.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/paths.rs6
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs5
-rw-r--r--src/tools/clippy/clippy_utils/src/source.rs10
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs10
-rw-r--r--src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs17
-rw-r--r--src/tools/clippy/clippy_utils/src/usage.rs13
15 files changed, 193 insertions, 120 deletions
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index 3926b954e..1596bb773 100644
--- a/src/tools/clippy/clippy_utils/Cargo.toml
+++ b/src/tools/clippy/clippy_utils/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "clippy_utils"
-version = "0.1.73"
+version = "0.1.74"
edition = "2021"
publish = false
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index 140cfa219..a78ff0202 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -166,7 +166,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
(Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r),
(Lit(l), Lit(r)) => l == r,
(Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt),
- (Let(lp, le, _), Let(rp, re, _)) => eq_pat(lp, rp) && eq_expr(le, re),
+ (Let(lp, le, _, _), Let(rp, re, _, _)) => eq_pat(lp, rp) && eq_expr(le, re),
(If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re),
(While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt),
(ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => {
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index 6d57af325..d596eed4b 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -21,7 +21,7 @@ use std::iter;
/// A `LitKind`-like enum to fold constant `Expr`s into.
#[derive(Debug, Clone)]
pub enum Constant<'tcx> {
- Adt(rustc_middle::mir::ConstantKind<'tcx>),
+ Adt(rustc_middle::mir::Const<'tcx>),
/// A `String` (e.g., "abc").
Str(String),
/// A binary string (e.g., `b"abc"`).
@@ -482,7 +482,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
.tcx
.const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), None)
.ok()
- .map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty))?;
+ .map(|val| rustc_middle::mir::Const::from_value(val, ty))?;
let result = miri_to_const(self.lcx, result)?;
self.source = ConstantSource::Constant;
Some(result)
@@ -655,10 +655,10 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
}
}
-pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'tcx>) -> Option<Constant<'tcx>> {
- use rustc_middle::mir::interpret::ConstValue;
+pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> {
+ use rustc_middle::mir::ConstValue;
match result {
- mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() {
+ mir::Const::Val(ConstValue::Scalar(Scalar::Int(int)), _) => match result.ty().kind() {
ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
@@ -671,47 +671,42 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'t
ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))),
_ => None,
},
- mir::ConstantKind::Val(ConstValue::Slice { data, start, end }, _) => match result.ty().kind() {
- ty::Ref(_, tam, _) => match tam.kind() {
- ty::Str => String::from_utf8(
- data.inner()
- .inspect_with_uninit_and_ptr_outside_interpreter(start..end)
- .to_owned(),
- )
- .ok()
- .map(Constant::Str),
- _ => None,
- },
- _ => None,
+ mir::Const::Val(cv, _) if matches!(result.ty().kind(), ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), ty::Str)) =>
+ {
+ let data = cv.try_get_slice_bytes_for_diagnostics(lcx.tcx)?;
+ String::from_utf8(data.to_owned()).ok().map(Constant::Str)
},
- mir::ConstantKind::Val(ConstValue::ByRef { alloc, offset: _ }, _) => match result.ty().kind() {
- ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
- ty::Array(sub_type, len) => match sub_type.kind() {
- ty::Float(FloatTy::F32) => match len.try_to_target_usize(lcx.tcx) {
- Some(len) => alloc
- .inner()
- .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap()))
- .to_owned()
- .array_chunks::<4>()
- .map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk))))
- .collect::<Option<Vec<Constant<'tcx>>>>()
- .map(Constant::Vec),
- _ => None,
- },
- ty::Float(FloatTy::F64) => match len.try_to_target_usize(lcx.tcx) {
- Some(len) => alloc
- .inner()
- .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap()))
- .to_owned()
- .array_chunks::<8>()
- .map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk))))
- .collect::<Option<Vec<Constant<'tcx>>>>()
- .map(Constant::Vec),
+ mir::Const::Val(ConstValue::Indirect { alloc_id, offset: _ }, _) => {
+ let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory();
+ match result.ty().kind() {
+ ty::Adt(adt_def, _) if adt_def.is_struct() => Some(Constant::Adt(result)),
+ ty::Array(sub_type, len) => match sub_type.kind() {
+ ty::Float(FloatTy::F32) => match len.try_to_target_usize(lcx.tcx) {
+ Some(len) => alloc
+ .inner()
+ .inspect_with_uninit_and_ptr_outside_interpreter(0..(4 * usize::try_from(len).unwrap()))
+ .to_owned()
+ .array_chunks::<4>()
+ .map(|&chunk| Some(Constant::F32(f32::from_le_bytes(chunk))))
+ .collect::<Option<Vec<Constant<'tcx>>>>()
+ .map(Constant::Vec),
+ _ => None,
+ },
+ ty::Float(FloatTy::F64) => match len.try_to_target_usize(lcx.tcx) {
+ Some(len) => alloc
+ .inner()
+ .inspect_with_uninit_and_ptr_outside_interpreter(0..(8 * usize::try_from(len).unwrap()))
+ .to_owned()
+ .array_chunks::<8>()
+ .map(|&chunk| Some(Constant::F64(f64::from_le_bytes(chunk))))
+ .collect::<Option<Vec<Constant<'tcx>>>>()
+ .map(Constant::Vec),
+ _ => None,
+ },
_ => None,
},
_ => None,
- },
- _ => None,
+ }
},
_ => None,
}
@@ -720,17 +715,17 @@ pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'t
fn field_of_struct<'tcx>(
adt_def: ty::AdtDef<'tcx>,
lcx: &LateContext<'tcx>,
- result: mir::ConstantKind<'tcx>,
+ result: mir::Const<'tcx>,
field: &Ident,
-) -> Option<mir::ConstantKind<'tcx>> {
- if let mir::ConstantKind::Val(result, ty) = result
- && let Some(dc) = rustc_const_eval::const_eval::try_destructure_mir_constant_for_diagnostics(lcx.tcx, result, ty)
+) -> Option<mir::Const<'tcx>> {
+ if let mir::Const::Val(result, ty) = result
+ && let Some(dc) = lcx.tcx.try_destructure_mir_constant_for_diagnostics(result, ty)
&& let Some(dc_variant) = dc.variant
&& let Some(variant) = adt_def.variants().get(dc_variant)
&& let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name)
&& let Some(&(val, ty)) = dc.fields.get(field_idx)
{
- Some(mir::ConstantKind::Val(val, ty))
+ Some(mir::Const::Val(val, ty))
}
else {
None
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index fdc35cd4d..52214e733 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -5,6 +5,7 @@ use crate::tokenize_with_text;
use rustc_ast::ast::InlineAsmTemplatePiece;
use rustc_data_structures::fx::FxHasher;
use rustc_hir::def::Res;
+use rustc_hir::MatchSource::TryDesugar;
use rustc_hir::{
ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg,
GenericArgs, Guard, HirId, HirIdMap, InlineAsmOperand, Let, Lifetime, LifetimeName, Pat, PatField, PatKind, Path,
@@ -311,7 +312,7 @@ impl HirEqInterExpr<'_, '_, '_> {
lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name)
},
(&ExprKind::Match(le, la, ref ls), &ExprKind::Match(re, ra, ref rs)) => {
- ls == rs
+ (ls == rs || (matches!((ls, rs), (TryDesugar(_), TryDesugar(_)))))
&& self.eq_expr(le, re)
&& over(la, ra, |l, r| {
self.eq_pat(l.pat, r.pat)
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 6c4cec595..13da79fba 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -5,6 +5,7 @@
#![feature(lint_reasons)]
#![feature(never_type)]
#![feature(rustc_private)]
+#![feature(assert_matches)]
#![recursion_limit = "512"]
#![cfg_attr(feature = "deny-warnings", deny(warnings))]
#![allow(clippy::missing_errors_doc, clippy::missing_panics_doc, clippy::must_use_candidate)]
@@ -89,14 +90,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,
- ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item,
+ ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, 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::mir::Const;
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc_middle::ty::binding::BindingMode;
use rustc_middle::ty::fast_reject::SimplifiedType;
@@ -110,6 +111,7 @@ use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::{sym, Span};
use rustc_target::abi::Integer;
+use visitors::Visitable;
use crate::consts::{constant, miri_to_const, Constant};
use crate::higher::Range;
@@ -286,7 +288,7 @@ pub fn is_wild(pat: &Pat<'_>) -> bool {
/// Checks if the given `QPath` belongs to a type alias.
pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
match *qpath {
- QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias { .. } | DefKind::AssocTy, ..)),
+ QPath::Resolved(_, path) => matches!(path.res, Res::Def(DefKind::TyAlias | DefKind::AssocTy, ..)),
QPath::TypeRelative(ty, _) if let TyKind::Path(qpath) = ty.kind => { is_ty_alias(&qpath) },
_ => false,
}
@@ -1286,7 +1288,7 @@ pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<
}
/// Returns `true` if `expr` contains a return expression
-pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
+pub fn contains_return<'tcx>(expr: impl Visitable<'tcx>) -> bool {
for_each_expr(expr, |e| {
if matches!(e.kind, hir::ExprKind::Ret(..)) {
ControlFlow::Break(())
@@ -1508,7 +1510,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
&& let bnd_ty = subst.type_at(0)
&& let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx)
&& let const_val = cx.tcx.valtree_to_const_val((bnd_ty, min_val.to_valtree()))
- && let min_const_kind = ConstantKind::from_value(const_val, bnd_ty)
+ && let min_const_kind = Const::from_value(const_val, bnd_ty)
&& let Some(min_const) = miri_to_const(cx, min_const_kind)
&& let Some(start_const) = constant(cx, cx.typeck_results(), start)
{
@@ -1524,7 +1526,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
&& let bnd_ty = subst.type_at(0)
&& let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx)
&& let const_val = cx.tcx.valtree_to_const_val((bnd_ty, max_val.to_valtree()))
- && let max_const_kind = ConstantKind::from_value(const_val, bnd_ty)
+ && let max_const_kind = Const::from_value(const_val, bnd_ty)
&& let Some(max_const) = miri_to_const(cx, max_const_kind)
&& let Some(end_const) = constant(cx, cx.typeck_results(), end)
{
@@ -1783,6 +1785,33 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc
None
}
+/// Returns `true` if the lint is `#[allow]`ed or `#[expect]`ed at any of the `ids`, fulfilling all
+/// of the expectations in `ids`
+///
+/// This should only be used when the lint would otherwise be emitted, for a way to check if a lint
+/// is allowed early to skip work see [`is_lint_allowed`]
+///
+/// To emit at a lint at a different context than the one current see
+/// [`span_lint_hir`](diagnostics::span_lint_hir) or
+/// [`span_lint_hir_and_then`](diagnostics::span_lint_hir_and_then)
+pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl IntoIterator<Item = HirId>) -> bool {
+ let mut suppress_lint = false;
+
+ for id in ids {
+ let (level, _) = cx.tcx.lint_level_at_node(lint, id);
+ if let Some(expectation) = level.get_expectation_id() {
+ cx.fulfill_expectation(expectation);
+ }
+
+ match level {
+ Level::Allow | Level::Expect(_) => suppress_lint = true,
+ Level::Warn | Level::ForceWarn(_) | Level::Deny | Level::Forbid => {},
+ }
+ }
+
+ suppress_lint
+}
+
/// Returns `true` if the lint is allowed in the current context. This is useful for
/// skipping long running code when it's unnecessary
///
@@ -1956,8 +1985,8 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>,
/// Checks if the given function kind is an async function.
pub fn is_async_fn(kind: FnKind<'_>) -> bool {
match kind {
- FnKind::ItemFn(_, _, header) => header.asyncness == IsAsync::Async,
- FnKind::Method(_, sig) => sig.header.asyncness == IsAsync::Async,
+ FnKind::ItemFn(_, _, header) => header.asyncness.is_async(),
+ FnKind::Method(_, sig) => sig.header.asyncness.is_async(),
FnKind::Closure => false,
}
}
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index 173f9841d..82508bcdb 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -10,8 +10,9 @@ use rustc_lint::LateContext;
use rustc_span::def_id::DefId;
use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol};
-use std::cell::RefCell;
+use std::cell::OnceCell;
use std::ops::ControlFlow;
+use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};
const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
@@ -374,28 +375,21 @@ thread_local! {
/// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an
/// assumption that the early pass that populates the map and the later late passes will all be
/// running on the same thread.
- static AST_FORMAT_ARGS: RefCell<FxHashMap<Span, FormatArgs>> = {
+ #[doc(hidden)]
+ pub static AST_FORMAT_ARGS: OnceCell<FxHashMap<Span, Rc<FormatArgs>>> = {
static CALLED: AtomicBool = AtomicBool::new(false);
debug_assert!(
!CALLED.swap(true, Ordering::SeqCst),
"incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread",
);
- RefCell::default()
+ OnceCell::new()
};
}
-/// Record [`rustc_ast::FormatArgs`] for use in late lint passes, this should only be called by
-/// `FormatArgsCollector`
-pub fn collect_ast_format_args(span: Span, format_args: &FormatArgs) {
- AST_FORMAT_ARGS.with(|ast_format_args| {
- ast_format_args.borrow_mut().insert(span, format_args.clone());
- });
-}
-
-/// Calls `callback` with an AST [`FormatArgs`] node if a `format_args` expansion is found as a
-/// descendant of `expn_id`
-pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId, callback: impl FnOnce(&FormatArgs)) {
+/// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of
+/// `expn_id`
+pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option<Rc<FormatArgs>> {
let format_args_expr = for_each_expr(start, |expr| {
let ctxt = expr.span.ctxt();
if ctxt.outer_expn().is_descendant_of(expn_id) {
@@ -410,13 +404,14 @@ pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId,
} else {
ControlFlow::Continue(Descend::No)
}
- });
+ })?;
- if let Some(expr) = format_args_expr {
- AST_FORMAT_ARGS.with(|ast_format_args| {
- ast_format_args.borrow().get(&expr.span).map(callback);
- });
- }
+ AST_FORMAT_ARGS.with(|ast_format_args| {
+ ast_format_args
+ .get()?
+ .get(&format_args_expr.span.with_parent(None))
+ .map(Rc::clone)
+ })
}
/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if
diff --git a/src/tools/clippy/clippy_utils/src/mir/mod.rs b/src/tools/clippy/clippy_utils/src/mir/mod.rs
index 131f3c0aa..9dbb4c68d 100644
--- a/src/tools/clippy/clippy_utils/src/mir/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/mir/mod.rs
@@ -1,7 +1,8 @@
use rustc_hir::{Expr, HirId};
+use rustc_index::bit_set::BitSet;
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::{
- traversal, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK,
+ traversal, BasicBlock, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK,
};
use rustc_middle::ty::TyCtxt;
@@ -29,20 +30,26 @@ pub fn visit_local_usage(locals: &[Local], mir: &Body<'_>, location: Location) -
locals.len()
];
- traversal::ReversePostorder::new(mir, location.block).try_fold(init, |usage, (tbb, tdata)| {
- // Give up on loops
- if tdata.terminator().successors().any(|s| s == location.block) {
- return None;
- }
+ traversal::Postorder::new(&mir.basic_blocks, location.block)
+ .collect::<Vec<_>>()
+ .into_iter()
+ .rev()
+ .try_fold(init, |usage, tbb| {
+ let tdata = &mir.basic_blocks[tbb];
- let mut v = V {
- locals,
- location,
- results: usage,
- };
- v.visit_basic_block_data(tbb, tdata);
- Some(v.results)
- })
+ // Give up on loops
+ if tdata.terminator().successors().any(|s| s == location.block) {
+ return None;
+ }
+
+ let mut v = V {
+ locals,
+ location,
+ results: usage,
+ };
+ v.visit_basic_block_data(tbb, tdata);
+ Some(v.results)
+ })
}
struct V<'a> {
@@ -79,8 +86,32 @@ impl<'a, 'tcx> Visitor<'tcx> for V<'a> {
}
}
+/// Checks if the block is part of a cycle
+pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool {
+ let mut seen = BitSet::new_empty(body.basic_blocks.len());
+ let mut to_visit = Vec::with_capacity(body.basic_blocks.len() / 2);
+
+ seen.insert(block);
+ let mut next = block;
+ loop {
+ for succ in body.basic_blocks[next].terminator().successors() {
+ if seen.insert(succ) {
+ to_visit.push(succ);
+ } else if succ == block {
+ return true;
+ }
+ }
+
+ if let Some(x) = to_visit.pop() {
+ next = x;
+ } else {
+ return false;
+ }
+ }
+}
+
/// Convenience wrapper around `visit_local_usage`.
-pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle::mir::Local) -> Option<bool> {
+pub fn used_exactly_once(mir: &Body<'_>, local: rustc_middle::mir::Local) -> Option<bool> {
visit_local_usage(
&[local],
mir,
@@ -91,11 +122,14 @@ pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle:
)
.map(|mut vec| {
let LocalUsage { local_use_locs, .. } = vec.remove(0);
- local_use_locs
+ let mut locations = local_use_locs
.into_iter()
- .filter(|location| !is_local_assignment(mir, local, *location))
- .count()
- == 1
+ .filter(|&location| !is_local_assignment(mir, local, location));
+ if let Some(location) = locations.next() {
+ locations.next().is_none() && !block_in_cycle(mir, location.block)
+ } else {
+ false
+ }
})
}
diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs
index 3637476c4..0faff05eb 100644
--- a/src/tools/clippy/clippy_utils/src/msrvs.rs
+++ b/src/tools/clippy/clippy_utils/src/msrvs.rs
@@ -20,7 +20,7 @@ macro_rules! msrv_aliases {
// names may refer to stabilized feature flags or library items
msrv_aliases! {
1,71,0 { TUPLE_ARRAY_CONVERSIONS }
- 1,70,0 { OPTION_IS_SOME_AND }
+ 1,70,0 { OPTION_IS_SOME_AND, BINARY_HEAP_RETAIN }
1,68,0 { PATH_MAIN_SEPARATOR_STR }
1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index 914ea85ac..2fb24b5c7 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -15,6 +15,7 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
];
#[cfg(feature = "internal")]
pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
+pub const BINARYHEAP_ITER: [&str; 5] = ["alloc", "collections", "binary_heap", "BinaryHeap", "iter"];
pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"];
@@ -48,6 +49,7 @@ pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
pub const INSERT_STR: [&str; 4] = ["alloc", "string", "String", "insert_str"];
pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
+pub const ITER_ONCE: [&str; 5] = ["core", "iter", "sources", "once", "Once"];
pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
#[cfg(feature = "internal")]
pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
@@ -86,10 +88,7 @@ pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"];
pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"];
pub const PTR_UNALIGNED_VOLATILE_LOAD: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_load"];
pub const PTR_UNALIGNED_VOLATILE_STORE: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_store"];
-pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"];
pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
-pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"];
-pub const PTR_WRITE_VOLATILE: [&str; 3] = ["core", "ptr", "write_volatile"];
pub const PUSH_STR: [&str; 4] = ["alloc", "string", "String", "push_str"];
pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
@@ -165,3 +164,4 @@ pub const DEBUG_STRUCT: [&str; 4] = ["core", "fmt", "builders", "DebugStruct"];
pub const ORD_CMP: [&str; 4] = ["core", "cmp", "Ord", "cmp"];
#[expect(clippy::invalid_paths)] // not sure why it thinks this, it works so
pub const BOOL_THEN: [&str; 4] = ["core", "bool", "<impl bool>", "then"];
+pub const ARRAY_INTO_ITER: [&str; 4] = ["core", "array", "iter", "IntoIter"];
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index 139e31bc5..55f9cb27a 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -272,6 +272,7 @@ fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &B
| ProjectionElem::Downcast(..)
| ProjectionElem::Subslice { .. }
| ProjectionElem::Deref
+ | ProjectionElem::Subtype(_)
| ProjectionElem::Index(_) => {},
}
}
@@ -291,8 +292,8 @@ fn check_terminator<'tcx>(
| TerminatorKind::FalseUnwind { .. }
| TerminatorKind::Goto { .. }
| TerminatorKind::Return
- | TerminatorKind::Resume
- | TerminatorKind::Terminate
+ | TerminatorKind::UnwindResume
+ | TerminatorKind::UnwindTerminate(_)
| TerminatorKind::Unreachable => Ok(()),
TerminatorKind::Drop { place, .. } => {
if !is_ty_const_destruct(tcx, place.ty(&body.local_decls, tcx).ty, body) {
diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs
index dc4ee7256..31cb42109 100644
--- a/src/tools/clippy/clippy_utils/src/source.rs
+++ b/src/tools/clippy/clippy_utils/src/source.rs
@@ -8,7 +8,7 @@ use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
use rustc_lint::{LateContext, LintContext};
use rustc_session::Session;
use rustc_span::source_map::{original_sp, SourceMap};
-use rustc_span::{hygiene, BytePos, Pos, SourceFile, Span, SpanData, SyntaxContext, DUMMY_SP};
+use rustc_span::{hygiene, BytePos, SourceFileAndLine, Pos, SourceFile, Span, SpanData, SyntaxContext, DUMMY_SP};
use std::borrow::Cow;
use std::ops::Range;
@@ -117,9 +117,9 @@ fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePo
/// ```
fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
let span = original_sp(span, DUMMY_SP);
- let source_map_and_line = cx.sess().source_map().lookup_line(span.lo()).unwrap();
- let line_no = source_map_and_line.line;
- let line_start = source_map_and_line.sf.lines(|lines| lines[line_no]);
+ let SourceFileAndLine { sf, line } = cx.sess().source_map().lookup_line(span.lo()).unwrap();
+ let line_start = sf.lines()[line];
+ let line_start = sf.absolute_position(line_start);
span.with_lo(line_start)
}
@@ -362,7 +362,7 @@ pub fn snippet_block_with_context<'a>(
}
/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
-/// will result in the macro call, rather then the expansion, if the span is from a child context.
+/// will result in the macro call, rather than the expansion, if the span is from a child context.
/// If the span is not from a child context, it will be used directly instead.
///
/// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index ee5a49a20..ae8ee371f 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -88,7 +88,7 @@ impl<'a> Sugg<'a> {
}
/// Same as `hir`, but first walks the span up to the given context. This will result in the
- /// macro call, rather then the expansion, if the span is from a child context. If the span is
+ /// macro call, rather than the expansion, if the span is from a child context. If the span is
/// not from a child context, it will be used directly instead.
///
/// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index a05f682aa..604dc7691 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -13,7 +13,8 @@ use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
-use rustc_middle::mir::interpret::{ConstValue, Scalar};
+use rustc_middle::mir::interpret::Scalar;
+use rustc_middle::mir::ConstValue;
use rustc_middle::traits::EvaluationResult;
use rustc_middle::ty::layout::ValidityRequirement;
use rustc_middle::ty::{
@@ -27,6 +28,7 @@ use rustc_target::abi::{Size, VariantIdx};
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
use rustc_trait_selection::traits::{Obligation, ObligationCause};
+use std::assert_matches::debug_assert_matches;
use std::iter;
use crate::{match_def_path, path_res, paths};
@@ -259,7 +261,11 @@ pub fn implements_trait_with_env_from_iter<'tcx>(
})),
);
- debug_assert_eq!(tcx.def_kind(trait_id), DefKind::Trait);
+ debug_assert_matches!(
+ tcx.def_kind(trait_id),
+ DefKind::Trait | DefKind::TraitAlias,
+ "`DefId` must belong to a trait or trait alias"
+ );
#[cfg(debug_assertions)]
assert_generic_args_match(tcx, trait_id, trait_ref.args);
diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
index 06fd95290..750646723 100644
--- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
@@ -150,7 +150,7 @@ fn generic_args_certainty(cx: &LateContext<'_>, args: &GenericArgs<'_>) -> Certa
}
/// Tries to tell whether a `QPath` resolves to something certain, e.g., whether all of its path
-/// segments generic arguments are are instantiated.
+/// segments generic arguments are instantiated.
///
/// `qpath` could refer to either a type or a value. The heuristic never needs the `DefId` of a
/// value. So `DefId`s are retained only when `resolves_to_type` is true.
@@ -207,8 +207,8 @@ fn path_segment_certainty(
// Checking `res_generics_def_id(..)` before calling `generics_of` avoids an ICE.
if cx.tcx.res_generics_def_id(path_segment.res).is_some() {
let generics = cx.tcx.generics_of(def_id);
- let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && generics.params.is_empty()
- {
+ let count = generics.params.len() - usize::from(generics.host_effect_index.is_some());
+ let lhs = if (parent_certainty.is_certain() || generics.parent_count == 0) && count == 0 {
Certainty::Certain(None)
} else {
Certainty::Uncertain
@@ -219,7 +219,7 @@ fn path_segment_certainty(
// See the comment preceding `qpath_certainty`. `def_id` could refer to a type or a value.
let certainty = lhs.join_clearing_def_ids(rhs);
if resolves_to_type {
- if let DefKind::TyAlias { .. } = cx.tcx.def_kind(def_id) {
+ if let DefKind::TyAlias = cx.tcx.def_kind(def_id) {
adt_def_id(cx.tcx.type_of(def_id).instantiate_identity())
.map_or(certainty, |def_id| certainty.with_def_id(def_id))
} else {
@@ -299,10 +299,11 @@ fn type_is_inferrable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> b
// Check that all type parameters appear in the functions input types.
(0..(generics.parent_count + generics.params.len()) as u32).all(|index| {
- fn_sig
- .inputs()
- .iter()
- .any(|input_ty| contains_param(*input_ty.skip_binder(), index))
+ Some(index as usize) == generics.host_effect_index
+ || fn_sig
+ .inputs()
+ .iter()
+ .any(|input_ty| contains_param(*input_ty.skip_binder(), index))
})
}
diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs
index 39ef76348..ec131c7f6 100644
--- a/src/tools/clippy/clippy_utils/src/usage.rs
+++ b/src/tools/clippy/clippy_utils/src/usage.rs
@@ -4,7 +4,7 @@ use core::ops::ControlFlow;
use hir::def::Res;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{self as hir, Expr, ExprKind, HirId, HirIdSet};
-use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
+use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, Place, PlaceBase, PlaceWithHirId};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
@@ -37,6 +37,17 @@ pub fn is_potentially_mutated<'tcx>(variable: HirId, expr: &'tcx Expr<'_>, cx: &
mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&variable))
}
+pub fn is_potentially_local_place(local_id: HirId, place: &Place<'_>) -> bool {
+ match place.base {
+ PlaceBase::Local(id) => id == local_id,
+ PlaceBase::Upvar(_) => {
+ // Conservatively assume yes.
+ true
+ },
+ _ => false,
+ }
+}
+
struct MutVarsDelegate {
used_mutably: HirIdSet,
skip: bool,