summaryrefslogtreecommitdiffstats
path: root/src/tools/clippy/clippy_utils
diff options
context:
space:
mode:
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/check_proc_macro.rs122
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs150
-rw-r--r--src/tools/clippy/clippy_utils/src/diagnostics.rs22
-rw-r--r--src/tools/clippy/clippy_utils/src/eager_or_lazy.rs28
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs3
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs23
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs14
-rw-r--r--src/tools/clippy/clippy_utils/src/mir/mod.rs23
-rw-r--r--src/tools/clippy/clippy_utils/src/msrvs.rs8
-rw-r--r--src/tools/clippy/clippy_utils/src/numeric_literal.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.rs123
-rw-r--r--src/tools/clippy/clippy_utils/src/source.rs13
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs10
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs82
-rw-r--r--src/tools/clippy/clippy_utils/src/usage.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs1
18 files changed, 449 insertions, 185 deletions
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index 66a5079fa..cfe686eb9 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.71"
+version = "0.1.72"
edition = "2021"
publish = false
diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
index 9edaae853..c6d0b654f 100644
--- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
+++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
@@ -12,25 +12,33 @@
//! code was written, and check if the span contains that text. Note this will only work correctly
//! if the span is not from a `macro_rules` based macro.
-use rustc_ast::ast::{IntTy, LitIntType, LitKind, StrStyle, UintTy};
+use rustc_ast::{
+ ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, UintTy},
+ token::CommentKind,
+ AttrStyle,
+};
use rustc_hir::{
intravisit::FnKind, Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, HirId,
- Impl, ImplItem, ImplItemKind, IsAuto, Item, ItemKind, LoopSource, MatchSource, Node, QPath, TraitItem,
- TraitItemKind, UnOp, UnsafeSource, Unsafety, Variant, VariantData, YieldSource,
+ Impl, ImplItem, ImplItemKind, IsAuto, Item, ItemKind, LoopSource, MatchSource, MutTy, Node, QPath, TraitItem,
+ TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Unsafety, Variant, VariantData, YieldSource,
};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty::TyCtxt;
use rustc_session::Session;
-use rustc_span::{Span, Symbol};
+use rustc_span::{symbol::Ident, Span, Symbol};
use rustc_target::spec::abi::Abi;
/// The search pattern to look for. Used by `span_matches_pat`
-#[derive(Clone, Copy)]
+#[derive(Clone)]
pub enum Pat {
/// A single string.
Str(&'static str),
+ /// A single string.
+ OwnedStr(String),
/// Any of the given strings.
MultiStr(&'static [&'static str]),
+ /// Any of the given strings.
+ OwnedMultiStr(Vec<String>),
/// The string representation of the symbol.
Sym(Symbol),
/// Any decimal or hexadecimal digit depending on the location.
@@ -51,12 +59,16 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) ->
let end_str = s.trim_end_matches(|c: char| c.is_whitespace() || c == ')' || c == ',');
(match start_pat {
Pat::Str(text) => start_str.starts_with(text),
+ Pat::OwnedStr(text) => start_str.starts_with(&text),
Pat::MultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
+ Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
Pat::Sym(sym) => start_str.starts_with(sym.as_str()),
Pat::Num => start_str.as_bytes().first().map_or(false, u8::is_ascii_digit),
} && match end_pat {
Pat::Str(text) => end_str.ends_with(text),
+ Pat::OwnedStr(text) => end_str.starts_with(&text),
Pat::MultiStr(texts) => texts.iter().any(|s| start_str.ends_with(s)),
+ Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
Pat::Sym(sym) => end_str.ends_with(sym.as_str()),
Pat::Num => end_str.as_bytes().last().map_or(false, u8::is_ascii_hexdigit),
})
@@ -271,14 +283,79 @@ fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirI
(start_pat, end_pat)
}
-pub trait WithSearchPat {
+fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) {
+ match attr.kind {
+ AttrKind::Normal(..) => {
+ let mut pat = if matches!(attr.style, AttrStyle::Outer) {
+ (Pat::Str("#["), Pat::Str("]"))
+ } else {
+ (Pat::Str("#!["), Pat::Str("]"))
+ };
+
+ if let Some(ident) = attr.ident() && let Pat::Str(old_pat) = pat.0 {
+ // TODO: I feel like it's likely we can use `Cow` instead but this will require quite a bit of
+ // refactoring
+ // NOTE: This will likely have false positives, like `allow = 1`
+ pat.0 = Pat::OwnedMultiStr(vec![ident.to_string(), old_pat.to_owned()]);
+ pat.1 = Pat::Str("");
+ }
+
+ pat
+ },
+ AttrKind::DocComment(_kind @ CommentKind::Line, ..) => {
+ if matches!(attr.style, AttrStyle::Outer) {
+ (Pat::Str("///"), Pat::Str(""))
+ } else {
+ (Pat::Str("//!"), Pat::Str(""))
+ }
+ },
+ AttrKind::DocComment(_kind @ CommentKind::Block, ..) => {
+ if matches!(attr.style, AttrStyle::Outer) {
+ (Pat::Str("/**"), Pat::Str("*/"))
+ } else {
+ (Pat::Str("/*!"), Pat::Str("*/"))
+ }
+ },
+ }
+}
+
+fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) {
+ match ty.kind {
+ TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")),
+ TyKind::Ptr(MutTy { mutbl, ty }) => (
+ if mutbl.is_mut() {
+ Pat::Str("*const")
+ } else {
+ Pat::Str("*mut")
+ },
+ ty_search_pat(ty).1,
+ ),
+ TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ty_search_pat(ty).1),
+ TyKind::BareFn(bare_fn) => (
+ Pat::OwnedStr(format!("{}{} fn", bare_fn.unsafety.prefix_str(), bare_fn.abi.name())),
+ ty_search_pat(ty).1,
+ ),
+ TyKind::Never => (Pat::Str("!"), Pat::Str("")),
+ TyKind::Tup(..) => (Pat::Str("("), Pat::Str(")")),
+ TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")),
+ TyKind::Path(qpath) => qpath_search_pat(&qpath),
+ // NOTE: This is missing `TraitObject`. It always return true then.
+ _ => (Pat::Str(""), Pat::Str("")),
+ }
+}
+
+fn ident_search_pat(ident: Ident) -> (Pat, Pat) {
+ (Pat::OwnedStr(ident.name.as_str().to_owned()), Pat::Str(""))
+}
+
+pub trait WithSearchPat<'cx> {
type Context: LintContext;
fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat);
fn span(&self) -> Span;
}
macro_rules! impl_with_search_pat {
($cx:ident: $ty:ident with $fn:ident $(($tcx:ident))?) => {
- impl<'cx> WithSearchPat for $ty<'cx> {
+ impl<'cx> WithSearchPat<'cx> for $ty<'cx> {
type Context = $cx<'cx>;
#[allow(unused_variables)]
fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat) {
@@ -297,8 +374,9 @@ impl_with_search_pat!(LateContext: TraitItem with trait_item_search_pat);
impl_with_search_pat!(LateContext: ImplItem with impl_item_search_pat);
impl_with_search_pat!(LateContext: FieldDef with field_def_search_pat);
impl_with_search_pat!(LateContext: Variant with variant_search_pat);
+impl_with_search_pat!(LateContext: Ty with ty_search_pat);
-impl<'cx> WithSearchPat for (&FnKind<'cx>, &Body<'cx>, HirId, Span) {
+impl<'cx> WithSearchPat<'cx> for (&FnKind<'cx>, &Body<'cx>, HirId, Span) {
type Context = LateContext<'cx>;
fn search_pat(&self, cx: &Self::Context) -> (Pat, Pat) {
@@ -310,11 +388,37 @@ impl<'cx> WithSearchPat for (&FnKind<'cx>, &Body<'cx>, HirId, Span) {
}
}
+// `Attribute` does not have the `hir` associated lifetime, so we cannot use the macro
+impl<'cx> WithSearchPat<'cx> for &'cx Attribute {
+ type Context = LateContext<'cx>;
+
+ fn search_pat(&self, _cx: &Self::Context) -> (Pat, Pat) {
+ attr_search_pat(self)
+ }
+
+ fn span(&self) -> Span {
+ self.span
+ }
+}
+
+// `Ident` does not have the `hir` associated lifetime, so we cannot use the macro
+impl<'cx> WithSearchPat<'cx> for Ident {
+ type Context = LateContext<'cx>;
+
+ fn search_pat(&self, _cx: &Self::Context) -> (Pat, Pat) {
+ ident_search_pat(*self)
+ }
+
+ fn span(&self) -> Span {
+ self.span
+ }
+}
+
/// Checks if the item likely came from a proc-macro.
///
/// This should be called after `in_external_macro` and the initial pattern matching of the ast as
/// it is significantly slower than both of those.
-pub fn is_from_proc_macro<T: WithSearchPat>(cx: &T::Context, item: &T) -> bool {
+pub fn is_from_proc_macro<'cx, T: WithSearchPat<'cx>>(cx: &T::Context, item: &T) -> bool {
let (start_pat, end_pat) = item.search_pat(cx);
!span_matches_pat(cx.sess(), item.span(), start_pat, end_pat)
}
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index fb772644c..d1cfdc496 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -6,7 +6,7 @@ use if_chain::if_chain;
use rustc_ast::ast::{self, LitFloatType, LitKind};
use rustc_data_structures::sync::Lrc;
use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{BinOp, BinOpKind, Block, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
+use rustc_hir::{BinOp, BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, QPath, UnOp};
use rustc_lexer::tokenize;
use rustc_lint::LateContext;
use rustc_middle::mir;
@@ -14,7 +14,7 @@ use rustc_middle::mir::interpret::Scalar;
use rustc_middle::ty::{self, EarlyBinder, FloatTy, ScalarInt, Ty, TyCtxt};
use rustc_middle::ty::{List, SubstsRef};
use rustc_middle::{bug, span_bug};
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::{Ident, Symbol};
use rustc_span::SyntaxContext;
use std::cmp::Ordering::{self, Equal};
use std::hash::{Hash, Hasher};
@@ -22,7 +22,8 @@ use std::iter;
/// A `LitKind`-like enum to fold constant `Expr`s into.
#[derive(Debug, Clone)]
-pub enum Constant {
+pub enum Constant<'tcx> {
+ Adt(rustc_middle::mir::ConstantKind<'tcx>),
/// A `String` (e.g., "abc").
Str(String),
/// A binary string (e.g., `b"abc"`).
@@ -38,20 +39,20 @@ pub enum Constant {
/// `true` or `false`.
Bool(bool),
/// An array of constants.
- Vec(Vec<Constant>),
+ Vec(Vec<Constant<'tcx>>),
/// Also an array, but with only one constant, repeated N times.
- Repeat(Box<Constant>, u64),
+ Repeat(Box<Constant<'tcx>>, u64),
/// A tuple of constants.
- Tuple(Vec<Constant>),
+ Tuple(Vec<Constant<'tcx>>),
/// A raw pointer.
RawPtr(u128),
/// A reference
- Ref(Box<Constant>),
+ Ref(Box<Constant<'tcx>>),
/// A literal with syntax error.
Err,
}
-impl PartialEq for Constant {
+impl<'tcx> PartialEq for Constant<'tcx> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Str(ls), Self::Str(rs)) => ls == rs,
@@ -80,13 +81,16 @@ impl PartialEq for Constant {
}
}
-impl Hash for Constant {
+impl<'tcx> Hash for Constant<'tcx> {
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
std::mem::discriminant(self).hash(state);
match *self {
+ Self::Adt(ref elem) => {
+ elem.hash(state);
+ },
Self::Str(ref s) => {
s.hash(state);
},
@@ -126,7 +130,7 @@ impl Hash for Constant {
}
}
-impl Constant {
+impl<'tcx> Constant<'tcx> {
pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
match (left, right) {
(Self::Str(ls), Self::Str(rs)) => Some(ls.cmp(rs)),
@@ -209,7 +213,7 @@ impl Constant {
}
/// Parses a `LitKind` to a `Constant`.
-pub fn lit_to_mir_constant(lit: &LitKind, ty: Option<Ty<'_>>) -> Constant {
+pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option<Ty<'tcx>>) -> Constant<'tcx> {
match *lit {
LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
LitKind::Byte(b) => Constant::Int(u128::from(b)),
@@ -248,7 +252,7 @@ pub fn constant<'tcx>(
lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>,
e: &Expr<'_>,
-) -> Option<Constant> {
+) -> Option<Constant<'tcx>> {
ConstEvalLateContext::new(lcx, typeck_results).expr(e)
}
@@ -257,7 +261,7 @@ pub fn constant_with_source<'tcx>(
lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>,
e: &Expr<'_>,
-) -> Option<(Constant, ConstantSource)> {
+) -> Option<(Constant<'tcx>, ConstantSource)> {
let mut ctxt = ConstEvalLateContext::new(lcx, typeck_results);
let res = ctxt.expr(e);
res.map(|x| (x, ctxt.source))
@@ -268,7 +272,7 @@ pub fn constant_simple<'tcx>(
lcx: &LateContext<'tcx>,
typeck_results: &ty::TypeckResults<'tcx>,
e: &Expr<'_>,
-) -> Option<Constant> {
+) -> Option<Constant<'tcx>> {
constant_with_source(lcx, typeck_results, e).and_then(|(c, s)| s.is_local().then_some(c))
}
@@ -338,8 +342,10 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
}
/// Simple constant folding: Insert an expression, get a constant or none.
- pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant> {
+ pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
match e.kind {
+ ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value),
+ ExprKind::DropTemps(e) => self.expr(e),
ExprKind::Path(ref qpath) => self.fetch_path(qpath, e.hir_id, self.typeck_results.expr_ty(e)),
ExprKind::Block(block, _) => self.block(block),
ExprKind::Lit(lit) => {
@@ -392,13 +398,25 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
},
ExprKind::Index(arr, index) => self.index(arr, index),
ExprKind::AddrOf(_, _, inner) => self.expr(inner).map(|r| Constant::Ref(Box::new(r))),
- // TODO: add other expressions.
+ ExprKind::Field(local_expr, ref field) => {
+ let result = self.expr(local_expr);
+ if let Some(Constant::Adt(constant)) = &self.expr(local_expr)
+ && let ty::Adt(adt_def, _) = constant.ty().kind()
+ && adt_def.is_struct()
+ && let Some(desired_field) = field_of_struct(*adt_def, self.lcx, *constant, field)
+ {
+ miri_to_const(self.lcx, desired_field)
+ }
+ else {
+ result
+ }
+ },
_ => None,
}
}
#[expect(clippy::cast_possible_wrap)]
- fn constant_not(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
+ fn constant_not(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option<Constant<'tcx>> {
use self::Constant::{Bool, Int};
match *o {
Bool(b) => Some(Bool(!b)),
@@ -414,7 +432,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
}
}
- fn constant_negate(&self, o: &Constant, ty: Ty<'_>) -> Option<Constant> {
+ fn constant_negate(&self, o: &Constant<'tcx>, ty: Ty<'_>) -> Option<Constant<'tcx>> {
use self::Constant::{Int, F32, F64};
match *o {
Int(value) => {
@@ -433,28 +451,25 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
/// Create `Some(Vec![..])` of all constants, unless there is any
/// non-constant part.
- fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant>> {
+ fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant<'tcx>>> {
vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
}
/// Lookup a possibly constant expression from an `ExprKind::Path`.
- fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant> {
+ fn fetch_path(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>) -> Option<Constant<'tcx>> {
let res = self.typeck_results.qpath_res(qpath, id);
match res {
Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
// Check if this constant is based on `cfg!(..)`,
// which is NOT constant for our purposes.
- if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id) &&
- let Node::Item(&Item {
- kind: ItemKind::Const(_, body_id),
- ..
- }) = node &&
- let Node::Expr(&Expr {
- kind: ExprKind::Lit(_),
- span,
- ..
- }) = self.lcx.tcx.hir().get(body_id.hir_id) &&
- is_direct_expn_of(span, "cfg").is_some() {
+ if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id)
+ && let Node::Item(Item { kind: ItemKind::Const(_, body_id), .. }) = node
+ && let Node::Expr(Expr { kind: ExprKind::Lit(_), span, .. }) = self.lcx
+ .tcx
+ .hir()
+ .get(body_id.hir_id)
+ && is_direct_expn_of(*span, "cfg").is_some()
+ {
return None;
}
@@ -462,25 +477,23 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
let substs = if self.substs.is_empty() {
substs
} else {
- EarlyBinder(substs).subst(self.lcx.tcx, self.substs)
+ EarlyBinder::bind(substs).subst(self.lcx.tcx, self.substs)
};
-
let result = self
.lcx
.tcx
.const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, substs), None)
.ok()
.map(|val| rustc_middle::mir::ConstantKind::from_value(val, ty))?;
- let result = miri_to_const(self.lcx.tcx, result)?;
+ let result = miri_to_const(self.lcx, result)?;
self.source = ConstantSource::Constant;
Some(result)
},
- // FIXME: cover all usable cases.
_ => None,
}
}
- fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant> {
+ fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant<'tcx>> {
let lhs = self.expr(lhs);
let index = self.expr(index);
@@ -506,7 +519,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
}
/// A block can only yield a constant if it only has one constant expression.
- fn block(&mut self, block: &Block<'_>) -> Option<Constant> {
+ fn block(&mut self, block: &Block<'_>) -> Option<Constant<'tcx>> {
if block.stmts.is_empty()
&& let Some(expr) = block.expr
{
@@ -539,7 +552,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
}
}
- fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant> {
+ fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant<'tcx>> {
if let Some(Constant::Bool(b)) = self.expr(cond) {
if b {
self.expr(then)
@@ -551,7 +564,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
}
}
- fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant> {
+ fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> {
let l = self.expr(left)?;
let r = self.expr(right);
match (l, r) {
@@ -644,23 +657,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
}
}
-pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) -> Option<Constant> {
+pub fn miri_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::ConstantKind<'tcx>) -> Option<Constant<'tcx>> {
use rustc_middle::mir::interpret::ConstValue;
match result {
- mir::ConstantKind::Val(ConstValue::Scalar(Scalar::Int(int)), _) => {
- match result.ty().kind() {
- ty::Bool => Some(Constant::Bool(int == ScalarInt::TRUE)),
- ty::Uint(_) | ty::Int(_) => Some(Constant::Int(int.assert_bits(int.size()))),
- ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
- int.try_into().expect("invalid f32 bit representation"),
- ))),
- ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
- int.try_into().expect("invalid f64 bit representation"),
- ))),
- ty::RawPtr(_) => Some(Constant::RawPtr(int.assert_bits(int.size()))),
- // FIXME: implement other conversions.
- _ => None,
- }
+ mir::ConstantKind::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()))),
+ ty::Float(FloatTy::F32) => Some(Constant::F32(f32::from_bits(
+ int.try_into().expect("invalid f32 bit representation"),
+ ))),
+ ty::Float(FloatTy::F64) => Some(Constant::F64(f64::from_bits(
+ int.try_into().expect("invalid f64 bit representation"),
+ ))),
+ 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() {
@@ -676,35 +687,54 @@ pub fn miri_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::ConstantKind<'tcx>) -
_ => None,
},
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.kind().try_to_target_usize(tcx) {
+ 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>>>()
+ .collect::<Option<Vec<Constant<'tcx>>>>()
.map(Constant::Vec),
_ => None,
},
- ty::Float(FloatTy::F64) => match len.kind().try_to_target_usize(tcx) {
+ 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>>>()
+ .collect::<Option<Vec<Constant<'tcx>>>>()
.map(Constant::Vec),
_ => None,
},
- // FIXME: implement other array type conversions.
_ => None,
},
_ => None,
},
- // FIXME: implement other conversions.
_ => None,
}
}
+
+fn field_of_struct<'tcx>(
+ adt_def: ty::AdtDef<'tcx>,
+ lcx: &LateContext<'tcx>,
+ result: mir::ConstantKind<'tcx>,
+ field: &Ident,
+) -> Option<mir::ConstantKind<'tcx>> {
+ if let mir::ConstantKind::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))
+ }
+ else {
+ None
+ }
+}
diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs
index 812f6fe71..edd87546a 100644
--- a/src/tools/clippy/clippy_utils/src/diagnostics.rs
+++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs
@@ -46,7 +46,7 @@ fn docs_link(diag: &mut Diagnostic, lint: &'static Lint) {
/// | ^^^^^^^^^^^^^^^^^^^^^^^
/// ```
pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<MultiSpan>, msg: &str) {
- cx.struct_span_lint(lint, sp, msg, |diag| {
+ cx.struct_span_lint(lint, sp, msg.to_string(), |diag| {
docs_link(diag, lint);
diag
});
@@ -80,11 +80,12 @@ pub fn span_lint_and_help<T: LintContext>(
help_span: Option<Span>,
help: &str,
) {
- cx.struct_span_lint(lint, span, msg, |diag| {
+ cx.struct_span_lint(lint, span, msg.to_string(), |diag| {
+ let help = help.to_string();
if let Some(help_span) = help_span {
- diag.span_help(help_span, help);
+ diag.span_help(help_span, help.to_string());
} else {
- diag.help(help);
+ diag.help(help.to_string());
}
docs_link(diag, lint);
diag
@@ -122,7 +123,8 @@ pub fn span_lint_and_note<T: LintContext>(
note_span: Option<Span>,
note: &str,
) {
- cx.struct_span_lint(lint, span, msg, |diag| {
+ cx.struct_span_lint(lint, span, msg.to_string(), |diag| {
+ let note = note.to_string();
if let Some(note_span) = note_span {
diag.span_note(note_span, note);
} else {
@@ -143,7 +145,7 @@ where
S: Into<MultiSpan>,
F: FnOnce(&mut Diagnostic),
{
- cx.struct_span_lint(lint, sp, msg, |diag| {
+ cx.struct_span_lint(lint, sp, msg.to_string(), |diag| {
f(diag);
docs_link(diag, lint);
diag
@@ -151,7 +153,7 @@ where
}
pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, sp: Span, msg: &str) {
- cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg, |diag| {
+ cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg.to_string(), |diag| {
docs_link(diag, lint);
diag
});
@@ -165,7 +167,7 @@ pub fn span_lint_hir_and_then(
msg: &str,
f: impl FnOnce(&mut Diagnostic),
) {
- cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg, |diag| {
+ cx.tcx.struct_span_lint_hir(lint, hir_id, sp, msg.to_string(), |diag| {
f(diag);
docs_link(diag, lint);
diag
@@ -202,7 +204,7 @@ pub fn span_lint_and_sugg<T: LintContext>(
applicability: Applicability,
) {
span_lint_and_then(cx, lint, sp, msg, |diag| {
- diag.span_suggestion(sp, help, sugg, applicability);
+ diag.span_suggestion(sp, help.to_string(), sugg, applicability);
});
}
@@ -232,5 +234,5 @@ pub fn multispan_sugg_with_applicability<I>(
) where
I: IntoIterator<Item = (Span, String)>,
{
- diag.multipart_suggestion(help_msg, sugg.into_iter().collect(), applicability);
+ diag.multipart_suggestion(help_msg.to_string(), sugg.into_iter().collect(), applicability);
}
diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
index 3df40942e..4a845ca63 100644
--- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
+++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
@@ -15,7 +15,8 @@ use rustc_hir::def::{DefKind, Res};
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{def_id::DefId, Block, Expr, ExprKind, QPath, UnOp};
use rustc_lint::LateContext;
-use rustc_middle::ty::{self, PredicateKind};
+use rustc_middle::ty;
+use rustc_middle::ty::adjustment::Adjust;
use rustc_span::{sym, Symbol};
use std::cmp;
use std::ops;
@@ -73,7 +74,7 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg:
.flat_map(|v| v.fields.iter())
.any(|x| matches!(cx.tcx.type_of(x.did).subst_identity().peel_refs().kind(), ty::Param(_)))
&& all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() {
- PredicateKind::Clause(ty::Clause::Trait(pred)) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker,
+ ty::ClauseKind::Trait(pred) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker,
_ => true,
})
&& subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_)))
@@ -114,6 +115,20 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
if self.eagerness == ForceNoChange {
return;
}
+
+ // Autoderef through a user-defined `Deref` impl can have side-effects,
+ // so don't suggest changing it.
+ if self
+ .cx
+ .typeck_results()
+ .expr_adjustments(e)
+ .iter()
+ .any(|adj| matches!(adj.kind, Adjust::Deref(Some(_))))
+ {
+ self.eagerness |= NoChange;
+ return;
+ }
+
match e.kind {
ExprKind::Call(
&Expr {
@@ -173,11 +188,15 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
self.eagerness = Lazy;
}
},
-
+ // Custom `Deref` impl might have side effects
+ ExprKind::Unary(UnOp::Deref, e)
+ if self.cx.typeck_results().expr_ty(e).builtin_deref(true).is_none() =>
+ {
+ self.eagerness |= NoChange;
+ },
// Dereferences should be cheap, but dereferencing a raw pointer earlier may not be safe.
ExprKind::Unary(UnOp::Deref, e) if !self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => (),
ExprKind::Unary(UnOp::Deref, _) => self.eagerness |= NoChange,
-
ExprKind::Unary(_, e)
if matches!(
self.cx.typeck_results().expr_ty(e).kind(),
@@ -191,6 +210,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
ExprKind::Break(..)
| ExprKind::Continue(_)
| ExprKind::Ret(_)
+ | ExprKind::Become(_)
| ExprKind::InlineAsm(_)
| ExprKind::Yield(..)
| ExprKind::Err(_) => {
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index a49246a78..3e1d73564 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -845,6 +845,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
self.hash_expr(e);
}
},
+ ExprKind::Become(f) => {
+ self.hash_expr(f);
+ },
ExprKind::Path(ref qpath) => {
self.hash_qpath(qpath);
},
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 8c883445a..727b59f1f 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -20,6 +20,7 @@
extern crate rustc_ast;
extern crate rustc_ast_pretty;
extern crate rustc_attr;
+extern crate rustc_const_eval;
extern crate rustc_data_structures;
// The `rustc_driver` crate seems to be required in order to use the `rust_ast` crate.
#[allow(unused_extern_crates)]
@@ -326,6 +327,18 @@ pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol)
.map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
}
+/// Checks if the `def_id` belongs to a function that is part of a trait impl.
+pub fn is_def_id_trait_method(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
+ if let Some(hir_id) = cx.tcx.opt_local_def_id_to_hir_id(def_id)
+ && let Node::Item(item) = cx.tcx.hir().get_parent(hir_id)
+ && let ItemKind::Impl(imp) = item.kind
+ {
+ imp.of_trait.is_some()
+ } else {
+ false
+ }
+}
+
/// Checks if the given expression is a path referring an item on the trait
/// that is marked with the given diagnostic item.
///
@@ -1498,7 +1511,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
&& 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 Some(min_const) = miri_to_const(cx.tcx, min_const_kind)
+ && let Some(min_const) = miri_to_const(cx, min_const_kind)
&& let Some(start_const) = constant(cx, cx.typeck_results(), start)
{
start_const == min_const
@@ -1514,7 +1527,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
&& 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 Some(max_const) = miri_to_const(cx.tcx, max_const_kind)
+ && let Some(max_const) = miri_to_const(cx, max_const_kind)
&& let Some(end_const) = constant(cx, cx.typeck_results(), end)
{
end_const == max_const
@@ -2396,7 +2409,7 @@ fn with_test_item_names(tcx: TyCtxt<'_>, module: LocalDefId, f: impl Fn(&[Symbol
/// Checks if the function containing the given `HirId` is a `#[test]` function
///
-/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
+/// Note: Add `//@compile-flags: --test` to UI tests with a `#[test]` function
pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
with_test_item_names(tcx, tcx.parent_module(id), |names| {
tcx.hir()
@@ -2418,7 +2431,7 @@ pub fn is_in_test_function(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
/// Checks if the item containing the given `HirId` has `#[cfg(test)]` attribute applied
///
-/// Note: Add `// compile-flags: --test` to UI tests with a `#[cfg(test)]` function
+/// Note: Add `//@compile-flags: --test` to UI tests with a `#[cfg(test)]` function
pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
fn is_cfg_test(attr: &Attribute) -> bool {
if attr.has_name(sym::cfg)
@@ -2440,7 +2453,7 @@ pub fn is_in_cfg_test(tcx: TyCtxt<'_>, id: hir::HirId) -> bool {
/// Checks whether item either has `test` attribute applied, or
/// is a module with `test` in its name.
///
-/// Note: Add `// compile-flags: --test` to UI tests with a `#[test]` function
+/// Note: Add `//@compile-flags: --test` to UI tests with a `#[test]` function
pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
is_in_test_function(tcx, item.hir_id())
|| matches!(item.kind, ItemKind::Mod(..))
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index e4a4936ff..00f3abaec 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -9,7 +9,7 @@ use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
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, Symbol};
+use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol};
use std::cell::RefCell;
use std::ops::ControlFlow;
use std::sync::atomic::{AtomicBool, Ordering};
@@ -415,8 +415,18 @@ pub fn find_format_arg_expr<'hir, 'ast>(
start: &'hir Expr<'hir>,
target: &'ast FormatArgument,
) -> Result<&'hir rustc_hir::Expr<'hir>, &'ast rustc_ast::Expr> {
+ let SpanData {
+ lo,
+ hi,
+ ctxt,
+ parent: _,
+ } = target.expr.span.data();
+
for_each_expr(start, |expr| {
- if expr.span == target.expr.span {
+ // When incremental compilation is enabled spans gain a parent during AST to HIR lowering,
+ // since we're comparing an AST span to a HIR one we need to ignore the parent field
+ let data = expr.span.data();
+ if data.lo == lo && data.hi == hi && data.ctxt == ctxt {
ControlFlow::Break(expr)
} else {
ControlFlow::Continue(())
diff --git a/src/tools/clippy/clippy_utils/src/mir/mod.rs b/src/tools/clippy/clippy_utils/src/mir/mod.rs
index 26c0015e8..131f3c0aa 100644
--- a/src/tools/clippy/clippy_utils/src/mir/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/mir/mod.rs
@@ -101,21 +101,26 @@ pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle:
/// Returns the `mir::Body` containing the node associated with `hir_id`.
#[allow(clippy::module_name_repetitions)]
-pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> &Body<'_> {
+pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<&Body<'_>> {
let body_owner_local_def_id = tcx.hir().enclosing_body_owner(hir_id);
- tcx.optimized_mir(body_owner_local_def_id.to_def_id())
+ if tcx.hir().body_owner_kind(body_owner_local_def_id).is_fn_or_closure() {
+ Some(tcx.optimized_mir(body_owner_local_def_id.to_def_id()))
+ } else {
+ None
+ }
}
/// Tries to determine the `Local` corresponding to `expr`, if any.
/// This function is expensive and should be used sparingly.
pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option<Local> {
- let mir = enclosing_mir(tcx, expr.hir_id);
- mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| {
- if local_decl.source_info.span == expr.span {
- Some(local)
- } else {
- None
- }
+ enclosing_mir(tcx, expr.hir_id).and_then(|mir| {
+ mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| {
+ if local_decl.source_info.span == expr.span {
+ Some(local)
+ } else {
+ None
+ }
+ })
})
}
diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs
index e05de2dc9..3637476c4 100644
--- a/src/tools/clippy/clippy_utils/src/msrvs.rs
+++ b/src/tools/clippy/clippy_utils/src/msrvs.rs
@@ -19,8 +19,10 @@ 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,68,0 { PATH_MAIN_SEPARATOR_STR }
- 1,65,0 { LET_ELSE }
+ 1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY }
1,55,0 { SEEK_REWIND }
@@ -28,7 +30,7 @@ msrv_aliases! {
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
1,50,0 { BOOL_THEN, CLAMP }
- 1,47,0 { TAU, IS_ASCII_DIGIT_CONST }
+ 1,47,0 { TAU, IS_ASCII_DIGIT_CONST, ARRAY_IMPL_ANY_LEN }
1,46,0 { CONST_IF_MATCH }
1,45,0 { STR_STRIP_PREFIX }
1,43,0 { LOG2_10, LOG10_2 }
@@ -42,11 +44,13 @@ msrv_aliases! {
1,34,0 { TRY_FROM }
1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
1,28,0 { FROM_BOOL }
+ 1,27,0 { ITERATOR_TRY_FOLD }
1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN }
1,24,0 { IS_ASCII_DIGIT }
1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
1,16,0 { STR_REPEAT }
+ 1,15,0 { MAYBE_BOUND_IN_WHERE }
}
fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
diff --git a/src/tools/clippy/clippy_utils/src/numeric_literal.rs b/src/tools/clippy/clippy_utils/src/numeric_literal.rs
index c225398ad..bbe4149fe 100644
--- a/src/tools/clippy/clippy_utils/src/numeric_literal.rs
+++ b/src/tools/clippy/clippy_utils/src/numeric_literal.rs
@@ -161,7 +161,7 @@ impl<'a> NumericLiteral<'a> {
}
if let Some((separator, exponent)) = self.exponent {
- if exponent != "0" {
+ if !exponent.is_empty() && exponent != "0" {
output.push_str(separator);
Self::group_digits(&mut output, exponent, group_size, true, false);
}
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index 0f0792fda..0e6f01287 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -15,7 +15,6 @@ pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
];
#[cfg(feature = "internal")]
pub const DIAGNOSTIC_BUILDER: [&str; 3] = ["rustc_errors", "diagnostic_builder", "DiagnosticBuilder"];
-pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
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"];
@@ -93,7 +92,6 @@ 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 RC_PTR_EQ: [&str; 4] = ["alloc", "rc", "Rc", "ptr_eq"];
pub const REFCELL_REF: [&str; 3] = ["core", "cell", "Ref"];
pub const REFCELL_REFMUT: [&str; 3] = ["core", "cell", "RefMut"];
pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
@@ -125,8 +123,6 @@ pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
pub const STR_BYTES: [&str; 4] = ["core", "str", "<impl str>", "bytes"];
pub const STR_CHARS: [&str; 4] = ["core", "str", "<impl str>", "chars"];
pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
-pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
-pub const STR_FROM_UTF8_UNCHECKED: [&str; 4] = ["core", "str", "converts", "from_utf8_unchecked"];
pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
#[cfg(feature = "internal")]
@@ -163,3 +159,5 @@ pub const VEC_IS_EMPTY: [&str; 4] = ["alloc", "vec", "Vec", "is_empty"];
pub const VEC_POP: [&str; 4] = ["alloc", "vec", "Vec", "pop"];
pub const OPTION_UNWRAP: [&str; 4] = ["core", "option", "Option", "unwrap"];
pub const OPTION_EXPECT: [&str; 4] = ["core", "option", "Option", "expect"];
+pub const FORMATTER: [&str; 3] = ["core", "fmt", "Formatter"];
+pub const DEBUG_STRUCT: [&str; 4] = ["core", "fmt", "builders", "DebugStruct"];
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 c0d2c835d..fbf4ab272 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
@@ -4,52 +4,30 @@
// differ from the time of `rustc` even if the name stays the same.
use crate::msrvs::Msrv;
+use hir::LangItem;
+use rustc_const_eval::transform::check_consts::ConstCx;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::traits::Obligation;
use rustc_middle::mir::{
Body, CastKind, NonDivergingIntrinsic, NullOp, Operand, Place, ProjectionElem, Rvalue, Statement, StatementKind,
Terminator, TerminatorKind,
};
+use rustc_middle::traits::{ImplSource, ObligationCause};
use rustc_middle::ty::subst::GenericArgKind;
-use rustc_middle::ty::{self, adjustment::PointerCast, Ty, TyCtxt};
+use rustc_middle::ty::{self, adjustment::PointerCoercion, Ty, TyCtxt};
+use rustc_middle::ty::{BoundConstness, TraitRef};
use rustc_semver::RustcVersion;
use rustc_span::symbol::sym;
use rustc_span::Span;
+use rustc_trait_selection::traits::{ObligationCtxt, SelectionContext};
use std::borrow::Cow;
type McfResult = Result<(), (Span, Cow<'static, str>)>;
pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv) -> McfResult {
let def_id = body.source.def_id();
- let mut current = def_id;
- loop {
- let predicates = tcx.predicates_of(current);
- for (predicate, _) in predicates.predicates {
- match predicate.kind().skip_binder() {
- ty::PredicateKind::Clause(
- ty::Clause::RegionOutlives(_)
- | ty::Clause::TypeOutlives(_)
- | ty::Clause::Projection(_)
- | ty::Clause::Trait(..)
- | ty::Clause::ConstArgHasType(..),
- )
- | ty::PredicateKind::WellFormed(_)
- | ty::PredicateKind::ConstEvaluatable(..)
- | ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
- ty::PredicateKind::AliasRelate(..) => panic!("alias relate predicate on function: {predicate:#?}"),
- ty::PredicateKind::ObjectSafe(_) => panic!("object safe predicate on function: {predicate:#?}"),
- ty::PredicateKind::ClosureKind(..) => panic!("closure kind predicate on function: {predicate:#?}"),
- ty::PredicateKind::Subtype(_) => panic!("subtype predicate on function: {predicate:#?}"),
- ty::PredicateKind::Coerce(_) => panic!("coerce predicate on function: {predicate:#?}"),
- ty::PredicateKind::Ambiguous => panic!("ambiguous predicate on function: {predicate:#?}"),
- }
- }
- match predicates.parent {
- Some(parent) => current = parent,
- None => break,
- }
- }
for local in &body.local_decls {
check_ty(tcx, local.ty, local.source_info.span)?;
@@ -61,7 +39,7 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv)
body.local_decls.iter().next().unwrap().source_info.span,
)?;
- for bb in body.basic_blocks.iter() {
+ for bb in &*body.basic_blocks {
check_terminator(tcx, body, bb.terminator(), msrv)?;
for stmt in &bb.statements {
check_statement(tcx, body, def_id, stmt)?;
@@ -89,7 +67,7 @@ fn check_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, span: Span) -> McfResult {
return Err((span, "function pointers in const fn are unstable".into()));
},
ty::Dynamic(preds, _, _) => {
- for pred in preds.iter() {
+ for pred in *preds {
match pred.skip_binder() {
ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
return Err((
@@ -141,18 +119,18 @@ fn check_rvalue<'tcx>(
| CastKind::FloatToFloat
| CastKind::FnPtrToPtr
| CastKind::PtrToPtr
- | CastKind::Pointer(PointerCast::MutToConstPointer | PointerCast::ArrayToPointer),
+ | CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer),
operand,
_,
) => check_operand(tcx, operand, span, body),
Rvalue::Cast(
- CastKind::Pointer(
- PointerCast::UnsafeFnPointer | PointerCast::ClosureFnPointer(_) | PointerCast::ReifyFnPointer,
+ CastKind::PointerCoercion(
+ PointerCoercion::UnsafeFnPointer | PointerCoercion::ClosureFnPointer(_) | PointerCoercion::ReifyFnPointer,
),
_,
_,
) => Err((span, "function pointer casts are not allowed in const fn".into())),
- Rvalue::Cast(CastKind::Pointer(PointerCast::Unsize), op, cast_ty) => {
+ Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), op, cast_ty) => {
let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
deref_ty.ty
} else {
@@ -256,7 +234,19 @@ fn check_statement<'tcx>(
fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
match operand {
- Operand::Move(place) | Operand::Copy(place) => check_place(tcx, *place, span, body),
+ Operand::Move(place) => {
+ if !place.projection.as_ref().is_empty()
+ && !is_ty_const_destruct(tcx, place.ty(&body.local_decls, tcx).ty, body)
+ {
+ return Err((
+ span,
+ "cannot drop locals with a non constant destructor in const fn".into(),
+ ));
+ }
+
+ check_place(tcx, *place, span, body)
+ },
+ Operand::Copy(place) => check_place(tcx, *place, span, body),
Operand::Constant(c) => match c.check_static_ptr(tcx) {
Some(_) => Err((span, "cannot access `static` items in const fn".into())),
None => Ok(()),
@@ -265,12 +255,10 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b
}
fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
- let mut cursor = place.projection.as_ref();
- while let [ref proj_base @ .., elem] = *cursor {
- cursor = proj_base;
+ for (base, elem) in place.as_ref().iter_projections() {
match elem {
ProjectionElem::Field(..) => {
- let base_ty = Place::ty_from(place.local, proj_base, body, tcx).ty;
+ let base_ty = base.ty(body, tcx).ty;
if let Some(def) = base_ty.ty_adt_def() {
// No union field accesses in `const fn`
if def.is_union() {
@@ -305,19 +293,23 @@ fn check_terminator<'tcx>(
| TerminatorKind::Resume
| TerminatorKind::Terminate
| TerminatorKind::Unreachable => Ok(()),
-
- TerminatorKind::Drop { place, .. } => check_place(tcx, *place, span, body),
-
+ TerminatorKind::Drop { place, .. } => {
+ if !is_ty_const_destruct(tcx, place.ty(&body.local_decls, tcx).ty, body) {
+ return Err((
+ span,
+ "cannot drop locals with a non constant destructor in const fn".into(),
+ ));
+ }
+ Ok(())
+ },
TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body),
-
TerminatorKind::GeneratorDrop | TerminatorKind::Yield { .. } => {
Err((span, "const fn generators are unstable".into()))
},
-
TerminatorKind::Call {
func,
args,
- from_hir_call: _,
+ call_source: _,
destination: _,
target: _,
unwind: _,
@@ -357,7 +349,6 @@ fn check_terminator<'tcx>(
Err((span, "can only call other const fns within const fn".into()))
}
},
-
TerminatorKind::Assert {
cond,
expected: _,
@@ -365,7 +356,6 @@ fn check_terminator<'tcx>(
target: _,
unwind: _,
} => check_operand(tcx, cond, span, body),
-
TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
}
}
@@ -379,8 +369,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
// as a part of an unimplemented MSRV check https://github.com/rust-lang/rust/issues/65262.
// HACK(nilstrieb): CURRENT_RUSTC_VERSION can return versions like 1.66.0-dev. `rustc-semver`
- // doesn't accept the `-dev` version number so we have to strip it
- // off.
+ // doesn't accept the `-dev` version number so we have to strip it off.
let short_version = since
.as_str()
.split('-')
@@ -398,3 +387,35 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
}
})
}
+
+#[expect(clippy::similar_names)] // bit too pedantic
+fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool {
+ // Avoid selecting for simple cases, such as builtin types.
+ if ty::util::is_trivially_const_drop(ty) {
+ return true;
+ }
+
+ let obligation = Obligation::new(
+ tcx,
+ ObligationCause::dummy_with_span(body.span),
+ ConstCx::new(tcx, body).param_env.with_const(),
+ TraitRef::from_lang_item(tcx, LangItem::Destruct, body.span, [ty]).with_constness(BoundConstness::ConstIfConst),
+ );
+
+ let infcx = tcx.infer_ctxt().build();
+ let mut selcx = SelectionContext::new(&infcx);
+ let Some(impl_src) = selcx.select(&obligation).ok().flatten() else {
+ return false;
+ };
+
+ if !matches!(
+ impl_src,
+ ImplSource::Builtin(_) | ImplSource::Param(_, ty::BoundConstness::ConstIfConst)
+ ) {
+ return false;
+ }
+
+ let ocx = ObligationCtxt::new(&infcx);
+ ocx.register_obligations(impl_src.nested_obligations());
+ ocx.select_all_or_error().is_empty()
+}
diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs
index 0f6029064..582337b47 100644
--- a/src/tools/clippy/clippy_utils/src/source.rs
+++ b/src/tools/clippy/clippy_utils/src/source.rs
@@ -4,7 +4,7 @@
use rustc_data_structures::sync::Lrc;
use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
use rustc_lint::{LateContext, LintContext};
use rustc_session::Session;
use rustc_span::source_map::{original_sp, SourceMap};
@@ -71,11 +71,16 @@ pub fn expr_block<T: LintContext>(
app: &mut Applicability,
) -> String {
let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app);
- if from_macro {
- format!("{{ {code} }}")
- } else if let ExprKind::Block(_, _) = expr.kind {
+ if !from_macro &&
+ let ExprKind::Block(block, _) = expr.kind &&
+ block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
+ {
format!("{code}")
} else {
+ // FIXME: add extra indent for the unsafe blocks:
+ // original code: unsafe { ... }
+ // result code: { unsafe { ... } }
+ // desired code: {\n unsafe { ... }\n}
format!("{{ {code} }}")
}
}
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index 14f7f0301..cf781e18c 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -147,6 +147,7 @@ impl<'a> Sugg<'a> {
| hir::ExprKind::Path(..)
| hir::ExprKind::Repeat(..)
| hir::ExprKind::Ret(..)
+ | hir::ExprKind::Become(..)
| hir::ExprKind::Struct(..)
| hir::ExprKind::Tup(..)
| hir::ExprKind::Err(_) => Sugg::NonParen(get_snippet(expr.span)),
@@ -211,6 +212,7 @@ impl<'a> Sugg<'a> {
| ast::ExprKind::Path(..)
| ast::ExprKind::Repeat(..)
| ast::ExprKind::Ret(..)
+ | ast::ExprKind::Become(..)
| ast::ExprKind::Yeet(..)
| ast::ExprKind::FormatArgs(..)
| ast::ExprKind::Struct(..)
@@ -741,7 +743,7 @@ impl<T: LintContext> DiagnosticExt<T> for rustc_errors::Diagnostic {
if let Some(indent) = indentation(cx, item) {
let span = item.with_hi(item.lo());
- self.span_suggestion(span, msg, format!("{attr}\n{indent}"), applicability);
+ self.span_suggestion(span, msg.to_string(), format!("{attr}\n{indent}"), applicability);
}
}
@@ -762,7 +764,7 @@ impl<T: LintContext> DiagnosticExt<T> for rustc_errors::Diagnostic {
})
.collect::<String>();
- self.span_suggestion(span, msg, format!("{new_item}\n{indent}"), applicability);
+ self.span_suggestion(span, msg.to_string(), format!("{new_item}\n{indent}"), applicability);
}
}
@@ -779,7 +781,7 @@ impl<T: LintContext> DiagnosticExt<T> for rustc_errors::Diagnostic {
}
}
- self.span_suggestion(remove_span, msg, "", applicability);
+ self.span_suggestion(remove_span, msg.to_string(), "", applicability);
}
}
@@ -942,7 +944,7 @@ impl<'tcx> Delegate<'tcx> for DerefDelegate<'_, 'tcx> {
},
// item is used in a call
// i.e.: `Call`: `|x| please(x)` or `MethodCall`: `|x| [1, 2, 3].contains(x)`
- ExprKind::Call(_, [call_args @ ..]) | ExprKind::MethodCall(_, _, [call_args @ ..], _) => {
+ ExprKind::Call(_, call_args) | ExprKind::MethodCall(_, _, call_args, _) => {
let expr = self.cx.tcx.hir().expect_expr(cmt.hir_id);
let arg_ty_kind = self.cx.typeck_results().expr_ty(expr).kind();
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 7b4ed77e8..d650cbe0b 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -17,8 +17,8 @@ use rustc_lint::LateContext;
use rustc_middle::mir::interpret::{ConstValue, Scalar};
use rustc_middle::ty::{
self, layout::ValidityRequirement, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, IntTy, List, ParamEnv,
- Predicate, PredicateKind, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
- TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr,
+ Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor,
+ UintTy, VariantDef, VariantDiscr,
};
use rustc_middle::ty::{GenericArg, GenericArgKind};
use rustc_span::symbol::Ident;
@@ -94,7 +94,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'
match predicate.kind().skip_binder() {
// For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through
// and check substitutions to find `U`.
- ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => {
+ ty::ClauseKind::Trait(trait_predicate) => {
if trait_predicate
.trait_ref
.substs
@@ -107,7 +107,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'
},
// For `impl Trait<Assoc=U>`, it will register a predicate of `<T as Trait>::Assoc = U`,
// so we check the term for `U`.
- ty::PredicateKind::Clause(ty::Clause::Projection(projection_predicate)) => {
+ ty::ClauseKind::Projection(projection_predicate) => {
if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() {
if contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) {
return true;
@@ -268,7 +268,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
ty::Tuple(substs) => substs.iter().any(|ty| is_must_use_ty(cx, ty)),
ty::Alias(ty::Opaque, ty::AliasTy { def_id, .. }) => {
for (predicate, _) in cx.tcx.explicit_item_bounds(def_id).skip_binder() {
- if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() {
+ if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() {
if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) {
return true;
}
@@ -277,7 +277,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
false
},
ty::Dynamic(binder, _, _) => {
- for predicate in binder.iter() {
+ for predicate in *binder {
if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
if cx.tcx.has_attr(trait_ref.def_id, sym::must_use) {
return true;
@@ -563,7 +563,7 @@ fn is_uninit_value_valid_for_ty_fallback<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'t
}
/// Gets an iterator over all predicates which apply to the given item.
-pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(Predicate<'_>, Span)> {
+pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(ty::Clause<'_>, Span)> {
let mut next_id = Some(id);
iter::from_fn(move || {
next_id.take().map(|id| {
@@ -665,7 +665,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'t
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => sig_from_bounds(
cx,
ty,
- cx.tcx.item_bounds(def_id).subst(cx.tcx, substs),
+ cx.tcx.item_bounds(def_id).subst_iter(cx.tcx, substs),
cx.tcx.opt_parent(def_id),
),
ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)),
@@ -698,7 +698,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'t
fn sig_from_bounds<'tcx>(
cx: &LateContext<'tcx>,
ty: Ty<'tcx>,
- predicates: &'tcx [Predicate<'tcx>],
+ predicates: impl IntoIterator<Item = ty::Clause<'tcx>>,
predicates_id: Option<DefId>,
) -> Option<ExprFnSig<'tcx>> {
let mut inputs = None;
@@ -707,7 +707,7 @@ fn sig_from_bounds<'tcx>(
for pred in predicates {
match pred.kind().skip_binder() {
- PredicateKind::Clause(ty::Clause::Trait(p))
+ ty::ClauseKind::Trait(p)
if (lang_items.fn_trait() == Some(p.def_id())
|| lang_items.fn_mut_trait() == Some(p.def_id())
|| lang_items.fn_once_trait() == Some(p.def_id()))
@@ -720,7 +720,7 @@ fn sig_from_bounds<'tcx>(
}
inputs = Some(i);
},
- PredicateKind::Clause(ty::Clause::Projection(p))
+ ty::ClauseKind::Projection(p)
if Some(p.projection_ty.def_id) == lang_items.fn_once_output() && p.projection_ty.self_ty() == ty =>
{
if output.is_some() {
@@ -747,7 +747,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option
.subst_iter_copied(cx.tcx, ty.substs)
{
match pred.kind().skip_binder() {
- PredicateKind::Clause(ty::Clause::Trait(p))
+ ty::ClauseKind::Trait(p)
if (lang_items.fn_trait() == Some(p.def_id())
|| lang_items.fn_mut_trait() == Some(p.def_id())
|| lang_items.fn_once_trait() == Some(p.def_id())) =>
@@ -760,9 +760,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option
}
inputs = Some(i);
},
- PredicateKind::Clause(ty::Clause::Projection(p))
- if Some(p.projection_ty.def_id) == lang_items.fn_once_output() =>
- {
+ ty::ClauseKind::Projection(p) if Some(p.projection_ty.def_id) == lang_items.fn_once_output() => {
if output.is_some() {
// Multiple different fn trait impls. Is this even allowed?
return None;
@@ -937,7 +935,7 @@ pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<
}
/// Checks if the type is a type parameter implementing `FnOnce`, but not `FnMut`.
-pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [Predicate<'_>]) -> bool {
+pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [ty::Clause<'_>]) -> bool {
let ty::Param(ty) = *ty.kind() else {
return false;
};
@@ -950,7 +948,7 @@ pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tc
predicates
.iter()
.try_fold(false, |found, p| {
- if let PredicateKind::Clause(ty::Clause::Trait(p)) = p.kind().skip_binder()
+ if let ty::ClauseKind::Trait(p) = p.kind().skip_binder()
&& let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
&& ty.index == self_ty.index
{
@@ -1126,7 +1124,7 @@ pub fn make_normalized_projection<'tcx>(
);
return None;
}
- match tcx.try_normalize_erasing_regions(param_env, tcx.mk_projection(ty.def_id, ty.substs)) {
+ match tcx.try_normalize_erasing_regions(param_env, Ty::new_projection(tcx,ty.def_id, ty.substs)) {
Ok(ty) => Some(ty),
Err(e) => {
debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}");
@@ -1180,3 +1178,51 @@ pub fn is_interior_mut_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
_ => false,
}
}
+
+pub fn make_normalized_projection_with_regions<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ParamEnv<'tcx>,
+ container_id: DefId,
+ assoc_ty: Symbol,
+ substs: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
+) -> Option<Ty<'tcx>> {
+ fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> {
+ #[cfg(debug_assertions)]
+ if let Some((i, subst)) = ty
+ .substs
+ .iter()
+ .enumerate()
+ .find(|(_, subst)| subst.has_late_bound_regions())
+ {
+ debug_assert!(
+ false,
+ "substs contain late-bound region at index `{i}` which can't be normalized.\n\
+ use `TyCtxt::erase_late_bound_regions`\n\
+ note: subst is `{subst:#?}`",
+ );
+ return None;
+ }
+ let cause = rustc_middle::traits::ObligationCause::dummy();
+ match tcx
+ .infer_ctxt()
+ .build()
+ .at(&cause, param_env)
+ .query_normalize(Ty::new_projection(tcx,ty.def_id, ty.substs))
+ {
+ Ok(ty) => Some(ty.value),
+ Err(e) => {
+ debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}");
+ None
+ },
+ }
+ }
+ helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, substs)?)
+}
+
+pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+ let cause = rustc_middle::traits::ObligationCause::dummy();
+ match tcx.infer_ctxt().build().at(&cause, param_env).query_normalize(ty) {
+ Ok(ty) => ty.value,
+ Err(_) => ty,
+ }
+}
diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs
index ab3976a13..985508521 100644
--- a/src/tools/clippy/clippy_utils/src/usage.rs
+++ b/src/tools/clippy/clippy_utils/src/usage.rs
@@ -82,7 +82,7 @@ pub struct ParamBindingIdCollector {
impl<'tcx> ParamBindingIdCollector {
fn collect_binding_hir_ids(body: &'tcx hir::Body<'tcx>) -> Vec<hir::HirId> {
let mut hir_ids: Vec<hir::HirId> = Vec::new();
- for param in body.params.iter() {
+ for param in body.params {
let mut finder = ParamBindingIdCollector {
binding_hir_ids: Vec::new(),
};
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index 5dcd71cef..8dafa723a 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -651,6 +651,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>(
// Either drops temporaries, jumps out of the current expression, or has no sub expression.
ExprKind::DropTemps(_)
| ExprKind::Ret(_)
+ | ExprKind::Become(_)
| ExprKind::Break(..)
| ExprKind::Yield(..)
| ExprKind::Block(..)