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/ast_utils.rs68
-rw-r--r--src/tools/clippy/clippy_utils/src/attrs.rs24
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs32
-rw-r--r--src/tools/clippy/clippy_utils/src/diagnostics.rs12
-rw-r--r--src/tools/clippy/clippy_utils/src/eager_or_lazy.rs28
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs51
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs355
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs12
-rw-r--r--src/tools/clippy/clippy_utils/src/msrvs.rs107
-rw-r--r--src/tools/clippy/clippy_utils/src/numeric_literal.rs6
-rw-r--r--src/tools/clippy/clippy_utils/src/paths.rs8
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs33
-rw-r--r--src/tools/clippy/clippy_utils/src/source.rs60
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs68
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs307
-rw-r--r--src/tools/clippy/clippy_utils/src/usage.rs9
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs12
18 files changed, 814 insertions, 380 deletions
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index 83fee7bb3..fb9f4740e 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.66"
+version = "0.1.67"
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 013399756..49e5f283d 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -75,11 +75,11 @@ pub fn eq_field_pat(l: &PatField, r: &PatField) -> bool {
&& over(&l.attrs, &r.attrs, eq_attr)
}
-pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool {
+pub fn eq_qself(l: &P<QSelf>, r: &P<QSelf>) -> bool {
l.position == r.position && eq_ty(&l.ty, &r.ty)
}
-pub fn eq_maybe_qself(l: &Option<QSelf>, r: &Option<QSelf>) -> bool {
+pub fn eq_maybe_qself(l: &Option<P<QSelf>>, r: &Option<P<QSelf>>) -> bool {
match (l, r) {
(Some(l), Some(r)) => eq_qself(l, r),
(None, None) => true,
@@ -147,12 +147,23 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
(Array(l), Array(r)) | (Tup(l), Tup(r)) => over(l, r, |l, r| eq_expr(l, r)),
(Repeat(le, ls), Repeat(re, rs)) => eq_expr(le, re) && eq_expr(&ls.value, &rs.value),
(Call(lc, la), Call(rc, ra)) => eq_expr(lc, rc) && over(la, ra, |l, r| eq_expr(l, r)),
- (MethodCall(lc, ls, la, _), MethodCall(rc, rs, ra, _)) => {
- eq_path_seg(lc, rc) && eq_expr(ls, rs) && over(la, ra, |l, r| eq_expr(l, r))
- },
+ (
+ MethodCall(box ast::MethodCall {
+ seg: ls,
+ receiver: lr,
+ args: la,
+ ..
+ }),
+ MethodCall(box ast::MethodCall {
+ seg: rs,
+ receiver: rr,
+ args: ra,
+ ..
+ }),
+ ) => eq_path_seg(ls, rs) && eq_expr(lr, rr) && over(la, ra, |l, r| eq_expr(l, r)),
(Binary(lo, ll, lr), Binary(ro, rl, rr)) => lo.node == ro.node && eq_expr(ll, rl) && eq_expr(lr, rr),
(Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r),
- (Lit(l), Lit(r)) => l.kind == r.kind,
+ (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),
(If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re),
@@ -160,7 +171,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
(ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => {
eq_label(ll, rl) && eq_pat(lp, rp) && eq_expr(li, ri) && eq_block(lt, rt)
},
- (Loop(lt, ll), Loop(rt, rl)) => eq_label(ll, rl) && eq_block(lt, rt),
+ (Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll, rl) && eq_block(lt, rt),
(Block(lb, ll), Block(rb, rl)) => eq_label(ll, rl) && eq_block(lb, rb),
(TryBlock(l), TryBlock(r)) => eq_block(l, r),
(Yield(l), Yield(r)) | (Ret(l), Ret(r)) => eq_expr_opt(l, r),
@@ -170,7 +181,26 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
(AssignOp(lo, lp, lv), AssignOp(ro, rp, rv)) => lo.node == ro.node && eq_expr(lp, rp) && eq_expr(lv, rv),
(Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
(Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm),
- (Closure(lb, lc, la, lm, lf, le, _), Closure(rb, rc, ra, rm, rf, re, _)) => {
+ (
+ Closure(box ast::Closure {
+ binder: lb,
+ capture_clause: lc,
+ asyncness: la,
+ movability: lm,
+ fn_decl: lf,
+ body: le,
+ ..
+ }),
+ Closure(box ast::Closure {
+ binder: rb,
+ capture_clause: rc,
+ asyncness: ra,
+ movability: rm,
+ fn_decl: rf,
+ body: re,
+ ..
+ }),
+ ) => {
eq_closure_binder(lb, rb)
&& lc == rc
&& la.is_async() == ra.is_async()
@@ -366,7 +396,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
&& over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
},
(MacCall(l), MacCall(r)) => eq_mac_call(l, r),
- (MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_mac_args(&l.body, &r.body),
+ (MacroDef(l), MacroDef(r)) => l.macro_rules == r.macro_rules && eq_delim_args(&l.body, &r.body),
_ => false,
}
}
@@ -536,7 +566,7 @@ pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
use UseTreeKind::*;
match (l, r) {
(Glob, Glob) => true,
- (Simple(l, _, _), Simple(r, _, _)) => both(l, r, |l, r| eq_id(*l, *r)),
+ (Simple(l), Simple(r)) => both(l, r, |l, r| eq_id(*l, *r)),
(Nested(l), Nested(r)) => over(l, r, |(l, _), (r, _)| eq_use_tree(l, r)),
_ => false,
}
@@ -687,7 +717,7 @@ pub fn eq_assoc_constraint(l: &AssocConstraint, r: &AssocConstraint) -> bool {
}
pub fn eq_mac_call(l: &MacCall, r: &MacCall) -> bool {
- eq_path(&l.path, &r.path) && eq_mac_args(&l.args, &r.args)
+ eq_path(&l.path, &r.path) && eq_delim_args(&l.args, &r.args)
}
pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
@@ -695,18 +725,22 @@ pub fn eq_attr(l: &Attribute, r: &Attribute) -> bool {
l.style == r.style
&& match (&l.kind, &r.kind) {
(DocComment(l1, l2), DocComment(r1, r2)) => l1 == r1 && l2 == r2,
- (Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_mac_args(&l.item.args, &r.item.args),
+ (Normal(l), Normal(r)) => eq_path(&l.item.path, &r.item.path) && eq_attr_args(&l.item.args, &r.item.args),
_ => false,
}
}
-pub fn eq_mac_args(l: &MacArgs, r: &MacArgs) -> bool {
- use MacArgs::*;
+pub fn eq_attr_args(l: &AttrArgs, r: &AttrArgs) -> bool {
+ use AttrArgs::*;
match (l, r) {
(Empty, Empty) => true,
- (Delimited(_, ld, lts), Delimited(_, rd, rts)) => ld == rd && lts.eq_unspanned(rts),
- (Eq(_, MacArgsEq::Ast(le)), Eq(_, MacArgsEq::Ast(re))) => eq_expr(le, re),
- (Eq(_, MacArgsEq::Hir(ll)), Eq(_, MacArgsEq::Hir(rl))) => ll.kind == rl.kind,
+ (Delimited(la), Delimited(ra)) => eq_delim_args(la, ra),
+ (Eq(_, AttrArgsEq::Ast(le)), Eq(_, AttrArgsEq::Ast(re))) => eq_expr(le, re),
+ (Eq(_, AttrArgsEq::Hir(ll)), Eq(_, AttrArgsEq::Hir(rl))) => ll.kind == rl.kind,
_ => false,
}
}
+
+pub fn eq_delim_args(l: &DelimArgs, r: &DelimArgs) -> bool {
+ l.delim == r.delim && l.tokens.eq_unspanned(&r.tokens)
+}
diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs
index cd8575c90..7987a233b 100644
--- a/src/tools/clippy/clippy_utils/src/attrs.rs
+++ b/src/tools/clippy/clippy_utils/src/attrs.rs
@@ -125,19 +125,19 @@ fn parse_attrs<F: FnMut(u64)>(sess: &Session, attrs: &[ast::Attribute], name: &'
}
}
-pub fn get_unique_inner_attr(sess: &Session, attrs: &[ast::Attribute], name: &'static str) -> Option<ast::Attribute> {
- let mut unique_attr = None;
+pub fn get_unique_attr<'a>(
+ sess: &'a Session,
+ attrs: &'a [ast::Attribute],
+ name: &'static str,
+) -> Option<&'a ast::Attribute> {
+ let mut unique_attr: Option<&ast::Attribute> = None;
for attr in get_attr(sess, attrs, name) {
- match attr.style {
- ast::AttrStyle::Inner if unique_attr.is_none() => unique_attr = Some(attr.clone()),
- ast::AttrStyle::Inner => {
- sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times"))
- .span_note(unique_attr.as_ref().unwrap().span, "first definition found here")
- .emit();
- },
- ast::AttrStyle::Outer => {
- sess.span_err(attr.span, format!("`{name}` cannot be an outer attribute"));
- },
+ if let Some(duplicate) = unique_attr {
+ sess.struct_span_err(attr.span, &format!("`{name}` is defined multiple times"))
+ .span_note(duplicate.span, "first definition found here")
+ .emit();
+ } else {
+ unique_attr = Some(attr);
}
}
unique_attr
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index 07e4ef6a2..315aea9aa 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -51,8 +51,8 @@ pub enum Constant {
impl PartialEq for Constant {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
- (&Self::Str(ref ls), &Self::Str(ref rs)) => ls == rs,
- (&Self::Binary(ref l), &Self::Binary(ref r)) => l == r,
+ (Self::Str(ls), Self::Str(rs)) => ls == rs,
+ (Self::Binary(l), Self::Binary(r)) => l == r,
(&Self::Char(l), &Self::Char(r)) => l == r,
(&Self::Int(l), &Self::Int(r)) => l == r,
(&Self::F64(l), &Self::F64(r)) => {
@@ -69,8 +69,8 @@ impl PartialEq for Constant {
},
(&Self::Bool(l), &Self::Bool(r)) => l == r,
(&Self::Vec(ref l), &Self::Vec(ref r)) | (&Self::Tuple(ref l), &Self::Tuple(ref r)) => l == r,
- (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => ls == rs && lv == rv,
- (&Self::Ref(ref lb), &Self::Ref(ref rb)) => *lb == *rb,
+ (Self::Repeat(lv, ls), Self::Repeat(rv, rs)) => ls == rs && lv == rv,
+ (Self::Ref(lb), Self::Ref(rb)) => *lb == *rb,
// TODO: are there inter-type equalities?
_ => false,
}
@@ -126,8 +126,8 @@ impl Hash for Constant {
impl Constant {
pub fn partial_cmp(tcx: TyCtxt<'_>, cmp_type: Ty<'_>, left: &Self, right: &Self) -> Option<Ordering> {
match (left, right) {
- (&Self::Str(ref ls), &Self::Str(ref rs)) => Some(ls.cmp(rs)),
- (&Self::Char(ref l), &Self::Char(ref r)) => Some(l.cmp(r)),
+ (Self::Str(ls), Self::Str(rs)) => Some(ls.cmp(rs)),
+ (Self::Char(l), Self::Char(r)) => Some(l.cmp(r)),
(&Self::Int(l), &Self::Int(r)) => match *cmp_type.kind() {
ty::Int(int_ty) => Some(sext(tcx, l, int_ty).cmp(&sext(tcx, r, int_ty))),
ty::Uint(_) => Some(l.cmp(&r)),
@@ -135,8 +135,8 @@ impl Constant {
},
(&Self::F64(l), &Self::F64(r)) => l.partial_cmp(&r),
(&Self::F32(l), &Self::F32(r)) => l.partial_cmp(&r),
- (&Self::Bool(ref l), &Self::Bool(ref r)) => Some(l.cmp(r)),
- (&Self::Tuple(ref l), &Self::Tuple(ref r)) if l.len() == r.len() => match *cmp_type.kind() {
+ (Self::Bool(l), Self::Bool(r)) => Some(l.cmp(r)),
+ (Self::Tuple(l), Self::Tuple(r)) if l.len() == r.len() => match *cmp_type.kind() {
ty::Tuple(tys) if tys.len() == l.len() => l
.iter()
.zip(r)
@@ -146,17 +146,16 @@ impl Constant {
.unwrap_or_else(|| Some(l.len().cmp(&r.len()))),
_ => None,
},
- (&Self::Vec(ref l), &Self::Vec(ref r)) => {
- let cmp_type = match *cmp_type.kind() {
- ty::Array(ty, _) | ty::Slice(ty) => ty,
- _ => return None,
+ (Self::Vec(l), Self::Vec(r)) => {
+ let (ty::Array(cmp_type, _) | ty::Slice(cmp_type)) = *cmp_type.kind() else {
+ return None
};
iter::zip(l, r)
.map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri))
.find(|r| r.map_or(true, |o| o != Ordering::Equal))
.unwrap_or_else(|| Some(l.len().cmp(&r.len())))
},
- (&Self::Repeat(ref lv, ref ls), &Self::Repeat(ref rv, ref rs)) => {
+ (Self::Repeat(lv, ls), Self::Repeat(rv, rs)) => {
match Self::partial_cmp(
tcx,
match *cmp_type.kind() {
@@ -170,7 +169,7 @@ impl Constant {
x => x,
}
},
- (&Self::Ref(ref lb), &Self::Ref(ref rb)) => Self::partial_cmp(
+ (Self::Ref(lb), Self::Ref(rb)) => Self::partial_cmp(
tcx,
match *cmp_type.kind() {
ty::Ref(_, ty, _) => ty,
@@ -401,10 +400,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
use self::Constant::{Int, F32, F64};
match *o {
Int(value) => {
- let ity = match *ty.kind() {
- ty::Int(ity) => ity,
- _ => return None,
- };
+ let ty::Int(ity) = *ty.kind() else { return None };
// sign extend
let value = sext(self.lcx.tcx, value, ity);
let value = value.checked_neg()?;
diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs
index 78f93755b..16b160b6f 100644
--- a/src/tools/clippy/clippy_utils/src/diagnostics.rs
+++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs
@@ -72,8 +72,8 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult
/// |
/// = help: consider using `f64::NAN` if you would like a constant representing NaN
/// ```
-pub fn span_lint_and_help<'a, T: LintContext>(
- cx: &'a T,
+pub fn span_lint_and_help<T: LintContext>(
+ cx: &T,
lint: &'static Lint,
span: impl Into<MultiSpan>,
msg: &str,
@@ -114,8 +114,8 @@ pub fn span_lint_and_help<'a, T: LintContext>(
/// 10 | forget(&SomeStruct);
/// | ^^^^^^^^^^^
/// ```
-pub fn span_lint_and_note<'a, T: LintContext>(
- cx: &'a T,
+pub fn span_lint_and_note<T: LintContext>(
+ cx: &T,
lint: &'static Lint,
span: impl Into<MultiSpan>,
msg: &str,
@@ -192,8 +192,8 @@ pub fn span_lint_hir_and_then(
/// = note: `-D fold-any` implied by `-D warnings`
/// ```
#[cfg_attr(feature = "internal", allow(clippy::collapsible_span_lint_calls))]
-pub fn span_lint_and_sugg<'a, T: LintContext>(
- cx: &'a T,
+pub fn span_lint_and_sugg<T: LintContext>(
+ cx: &T,
lint: &'static Lint,
sp: Span,
msg: &str,
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 95b3e651e..967119369 100644
--- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
+++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
@@ -73,7 +73,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).peel_refs().kind(), ty::Param(_)))
&& all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() {
- PredicateKind::Trait(pred) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker,
+ PredicateKind::Clause(ty::Clause::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(_)))
@@ -91,6 +91,16 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg:
}
}
+fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+ if let Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) = res {
+ cx.typeck_results()
+ .expr_ty(e)
+ .has_significant_drop(cx.tcx, cx.param_env)
+ } else {
+ false
+ }
+}
+
#[expect(clippy::too_many_lines)]
fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion {
struct V<'cx, 'tcx> {
@@ -113,13 +123,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
},
args,
) => match self.cx.qpath_res(path, hir_id) {
- Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_) => {
- if self
- .cx
- .typeck_results()
- .expr_ty(e)
- .has_significant_drop(self.cx.tcx, self.cx.param_env)
- {
+ res @ (Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_)) => {
+ if res_has_significant_drop(res, self.cx, e) {
self.eagerness = ForceNoChange;
return;
}
@@ -147,6 +152,12 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
self.eagerness |= NoChange;
return;
},
+ ExprKind::Path(ref path) => {
+ if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) {
+ self.eagerness = ForceNoChange;
+ return;
+ }
+ },
ExprKind::MethodCall(name, ..) => {
self.eagerness |= self
.cx
@@ -206,7 +217,6 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
| ExprKind::Match(..)
| ExprKind::Closure { .. }
| ExprKind::Field(..)
- | ExprKind::Path(_)
| ExprKind::AddrOf(..)
| ExprKind::Struct(..)
| ExprKind::Repeat(..)
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index cf24ec8b6..07fb6af91 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -7,8 +7,8 @@ use rustc_hir::def::Res;
use rustc_hir::HirIdMap;
use rustc_hir::{
ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg,
- GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path,
- PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
+ GenericArgs, Guard, HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, Pat, PatField, PatKind, Path,
+ PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
};
use rustc_lexer::{tokenize, TokenKind};
use rustc_lint::LateContext;
@@ -113,7 +113,7 @@ impl HirEqInterExpr<'_, '_, '_> {
}
}
- // eq_pat adds the HirIds to the locals map. We therefor call it last to make sure that
+ // eq_pat adds the HirIds to the locals map. We therefore call it last to make sure that
// these only get added if the init and type is equal.
both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
&& both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r))
@@ -131,13 +131,10 @@ impl HirEqInterExpr<'_, '_, '_> {
([], None, [], None) => {
// For empty blocks, check to see if the tokens are equal. This will catch the case where a macro
// expanded to nothing, or the cfg attribute was used.
- let (left, right) = match (
+ let (Some(left), Some(right)) = (
snippet_opt(self.inner.cx, left.span),
snippet_opt(self.inner.cx, right.span),
- ) {
- (Some(left), Some(right)) => (left, right),
- _ => return true,
- };
+ ) else { return true };
let mut left_pos = 0;
let left = tokenize(&left)
.map(|t| {
@@ -269,7 +266,7 @@ impl HirEqInterExpr<'_, '_, '_> {
(&ExprKind::Let(l), &ExprKind::Let(r)) => {
self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init)
},
- (&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
+ (ExprKind::Lit(l), ExprKind::Lit(r)) => l.node == r.node,
(&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => {
lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name)
},
@@ -294,8 +291,8 @@ impl HirEqInterExpr<'_, '_, '_> {
(&ExprKind::Repeat(le, ll), &ExprKind::Repeat(re, rl)) => {
self.eq_expr(le, re) && self.eq_array_length(ll, rl)
},
- (&ExprKind::Ret(ref l), &ExprKind::Ret(ref r)) => both(l, r, |l, r| self.eq_expr(l, r)),
- (&ExprKind::Path(ref l), &ExprKind::Path(ref r)) => self.eq_qpath(l, r),
+ (ExprKind::Ret(l), ExprKind::Ret(r)) => both(l, r, |l, r| self.eq_expr(l, r)),
+ (ExprKind::Path(l), ExprKind::Path(r)) => self.eq_qpath(l, r),
(&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => {
self.eq_qpath(l_path, r_path)
&& both(lo, ro, |l, r| self.eq_expr(l, r))
@@ -340,7 +337,7 @@ impl HirEqInterExpr<'_, '_, '_> {
}
fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool {
- left.name == right.name
+ left.res == right.res
}
fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool {
@@ -365,7 +362,7 @@ impl HirEqInterExpr<'_, '_, '_> {
}
eq
},
- (&PatKind::Path(ref l), &PatKind::Path(ref r)) => self.eq_qpath(l, r),
+ (PatKind::Path(l), PatKind::Path(r)) => self.eq_qpath(l, r),
(&PatKind::Lit(l), &PatKind::Lit(r)) => self.eq_expr(l, r),
(&PatKind::Tuple(l, ls), &PatKind::Tuple(r, rs)) => ls == rs && over(l, r, |l, r| self.eq_pat(l, r)),
(&PatKind::Range(ref ls, ref le, li), &PatKind::Range(ref rs, ref re, ri)) => {
@@ -432,13 +429,11 @@ impl HirEqInterExpr<'_, '_, '_> {
match (&left.kind, &right.kind) {
(&TyKind::Slice(l_vec), &TyKind::Slice(r_vec)) => self.eq_ty(l_vec, r_vec),
(&TyKind::Array(lt, ll), &TyKind::Array(rt, rl)) => self.eq_ty(lt, rt) && self.eq_array_length(ll, rl),
- (&TyKind::Ptr(ref l_mut), &TyKind::Ptr(ref r_mut)) => {
- l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty)
- },
- (&TyKind::Rptr(_, ref l_rmut), &TyKind::Rptr(_, ref r_rmut)) => {
+ (TyKind::Ptr(l_mut), TyKind::Ptr(r_mut)) => l_mut.mutbl == r_mut.mutbl && self.eq_ty(l_mut.ty, r_mut.ty),
+ (TyKind::Rptr(_, l_rmut), TyKind::Rptr(_, r_rmut)) => {
l_rmut.mutbl == r_rmut.mutbl && self.eq_ty(l_rmut.ty, r_rmut.ty)
},
- (&TyKind::Path(ref l), &TyKind::Path(ref r)) => self.eq_qpath(l, r),
+ (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r),
(&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)),
(&TyKind::Infer, &TyKind::Infer) => true,
_ => false,
@@ -930,16 +925,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
}
pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
- std::mem::discriminant(&lifetime.name).hash(&mut self.s);
- if let LifetimeName::Param(param_id, ref name) = lifetime.name {
- std::mem::discriminant(name).hash(&mut self.s);
+ lifetime.ident.name.hash(&mut self.s);
+ std::mem::discriminant(&lifetime.res).hash(&mut self.s);
+ if let LifetimeName::Param(param_id) = lifetime.res {
param_id.hash(&mut self.s);
- match name {
- ParamName::Plain(ref ident) => {
- ident.name.hash(&mut self.s);
- },
- ParamName::Fresh | ParamName::Error => {},
- }
}
}
@@ -1033,6 +1022,14 @@ pub fn hash_stmt(cx: &LateContext<'_>, s: &Stmt<'_>) -> u64 {
h.finish()
}
+pub fn is_bool(ty: &Ty<'_>) -> bool {
+ if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
+ matches!(path.res, Res::PrimTy(PrimTy::Bool))
+ } else {
+ false
+ }
+}
+
pub fn hash_expr(cx: &LateContext<'_>, e: &Expr<'_>) -> u64 {
let mut h = SpanlessHash::new(cx);
h.hash_expr(e);
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 3ebfc5e00..90192f46c 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -66,7 +66,7 @@ pub mod visitors;
pub use self::attrs::*;
pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
pub use self::hir_utils::{
- both, count_eq, eq_expr_value, hash_expr, hash_stmt, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
+ both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over, HirEqInterExpr, SpanlessEq, SpanlessHash,
};
use core::ops::ControlFlow;
@@ -80,17 +80,16 @@ use rustc_ast::ast::{self, LitKind};
use rustc_ast::Attribute;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::unhash::UnhashMap;
-use rustc_hir as hir;
-use rustc_hir::def::{DefKind, Namespace, Res};
-use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
use rustc_hir::hir_id::{HirIdMap, HirIdSet};
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
use rustc_hir::{
- def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination, Expr,
- ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource,
- Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
- TraitRef, TyKind, UnOp,
+ self as hir, def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Closure, Constness, Destination,
+ Expr, ExprKind, FnDecl, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, IsAsync, Item, ItemKind, LangItem, Local,
+ MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
+ TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp,
};
use rustc_lexer::{tokenize, TokenKind};
use rustc_lint::{LateContext, Level, Lint, LintContext};
@@ -106,50 +105,28 @@ use rustc_middle::ty::{
layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
};
use rustc_middle::ty::{FloatTy, IntTy, UintTy};
-use rustc_semver::RustcVersion;
-use rustc_session::Session;
use rustc_span::hygiene::{ExpnKind, MacroKind};
-use rustc_span::source_map::original_sp;
use rustc_span::source_map::SourceMap;
use rustc_span::sym;
-use rustc_span::symbol::{kw, Symbol};
-use rustc_span::{Span, DUMMY_SP};
+use rustc_span::symbol::{kw, Ident, Symbol};
+use rustc_span::Span;
use rustc_target::abi::Integer;
use crate::consts::{constant, Constant};
use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
use crate::visitors::for_each_expr;
-pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
- if let Ok(version) = RustcVersion::parse(msrv) {
- return Some(version);
- } else if let Some(sess) = sess {
- if let Some(span) = span {
- sess.span_err(span, format!("`{msrv}` is not a valid Rust version"));
- }
- }
- None
-}
-
-pub fn meets_msrv(msrv: Option<RustcVersion>, lint_msrv: RustcVersion) -> bool {
- msrv.map_or(true, |msrv| msrv.meets(lint_msrv))
-}
-
#[macro_export]
macro_rules! extract_msrv_attr {
($context:ident) => {
fn enter_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
let sess = rustc_lint::LintContext::sess(cx);
- match $crate::get_unique_inner_attr(sess, attrs, "msrv") {
- Some(msrv_attr) => {
- if let Some(msrv) = msrv_attr.value_str() {
- self.msrv = $crate::parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
- } else {
- sess.span_err(msrv_attr.span, "bad clippy attribute");
- }
- },
- _ => (),
- }
+ self.msrv.enter_lint_attrs(sess, attrs);
+ }
+
+ fn exit_lint_attrs(&mut self, cx: &rustc_lint::$context<'_>, attrs: &[rustc_ast::ast::Attribute]) {
+ let sess = rustc_lint::LintContext::sess(cx);
+ self.msrv.exit_lint_attrs(sess, attrs);
}
};
}
@@ -247,7 +224,7 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
/// For example, use this to check whether a function call or a pattern is `Some(..)`.
pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool {
if let Res::Def(DefKind::Ctor(..), id) = res
- && let Ok(lang_id) = cx.tcx.lang_items().require(lang_item)
+ && let Some(lang_id) = cx.tcx.lang_items().get(lang_item)
&& let Some(id) = cx.tcx.opt_parent(id)
{
id == lang_id
@@ -303,7 +280,7 @@ pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) ->
_ => did,
};
- cx.tcx.lang_items().require(item).map_or(false, |id| id == did)
+ cx.tcx.lang_items().get(item) == Some(did)
}
pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
@@ -435,6 +412,12 @@ pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[
}
/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
+/// it matches the given lang item.
+pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
+ path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.lang_items().get(lang_item) == Some(id))
+}
+
+/// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
/// it matches the given diagnostic item.
pub fn is_path_diagnostic_item<'tcx>(
cx: &LateContext<'_>,
@@ -525,125 +508,177 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>
path_res(cx, maybe_path).opt_def_id()
}
-fn find_primitive<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
- let single = |ty| tcx.incoherent_impls(ty).iter().copied();
- let empty = || [].iter().copied();
- match name {
- "bool" => single(BoolSimplifiedType),
- "char" => single(CharSimplifiedType),
- "str" => single(StrSimplifiedType),
- "array" => single(ArraySimplifiedType),
- "slice" => single(SliceSimplifiedType),
+fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
+ let ty = match name {
+ "bool" => BoolSimplifiedType,
+ "char" => CharSimplifiedType,
+ "str" => StrSimplifiedType,
+ "array" => ArraySimplifiedType,
+ "slice" => SliceSimplifiedType,
// FIXME: rustdoc documents these two using just `pointer`.
//
// Maybe this is something we should do here too.
- "const_ptr" => single(PtrSimplifiedType(Mutability::Not)),
- "mut_ptr" => single(PtrSimplifiedType(Mutability::Mut)),
- "isize" => single(IntSimplifiedType(IntTy::Isize)),
- "i8" => single(IntSimplifiedType(IntTy::I8)),
- "i16" => single(IntSimplifiedType(IntTy::I16)),
- "i32" => single(IntSimplifiedType(IntTy::I32)),
- "i64" => single(IntSimplifiedType(IntTy::I64)),
- "i128" => single(IntSimplifiedType(IntTy::I128)),
- "usize" => single(UintSimplifiedType(UintTy::Usize)),
- "u8" => single(UintSimplifiedType(UintTy::U8)),
- "u16" => single(UintSimplifiedType(UintTy::U16)),
- "u32" => single(UintSimplifiedType(UintTy::U32)),
- "u64" => single(UintSimplifiedType(UintTy::U64)),
- "u128" => single(UintSimplifiedType(UintTy::U128)),
- "f32" => single(FloatSimplifiedType(FloatTy::F32)),
- "f64" => single(FloatSimplifiedType(FloatTy::F64)),
- _ => empty(),
- }
-}
-
-/// Resolves a def path like `std::vec::Vec`. `namespace_hint` can be supplied to disambiguate
-/// between `std::vec` the module and `std::vec` the macro
-///
-/// This function is expensive and should be used sparingly.
-pub fn def_path_res(cx: &LateContext<'_>, path: &[&str], namespace_hint: Option<Namespace>) -> Res {
- fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: &str, matches_ns: impl Fn(Res) -> bool) -> Option<Res> {
- match tcx.def_kind(def_id) {
- DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
- .module_children(def_id)
- .iter()
- .find(|item| item.ident.name.as_str() == name && matches_ns(item.res.expect_non_local()))
- .map(|child| child.res.expect_non_local()),
- DefKind::Impl => tcx
- .associated_item_def_ids(def_id)
- .iter()
- .copied()
- .find(|assoc_def_id| tcx.item_name(*assoc_def_id).as_str() == name)
- .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id)),
- DefKind::Struct | DefKind::Union => tcx
- .adt_def(def_id)
- .non_enum_variant()
- .fields
- .iter()
- .find(|f| f.name.as_str() == name)
- .map(|f| Res::Def(DefKind::Field, f.did)),
- _ => None,
+ "const_ptr" => PtrSimplifiedType(Mutability::Not),
+ "mut_ptr" => PtrSimplifiedType(Mutability::Mut),
+ "isize" => IntSimplifiedType(IntTy::Isize),
+ "i8" => IntSimplifiedType(IntTy::I8),
+ "i16" => IntSimplifiedType(IntTy::I16),
+ "i32" => IntSimplifiedType(IntTy::I32),
+ "i64" => IntSimplifiedType(IntTy::I64),
+ "i128" => IntSimplifiedType(IntTy::I128),
+ "usize" => UintSimplifiedType(UintTy::Usize),
+ "u8" => UintSimplifiedType(UintTy::U8),
+ "u16" => UintSimplifiedType(UintTy::U16),
+ "u32" => UintSimplifiedType(UintTy::U32),
+ "u64" => UintSimplifiedType(UintTy::U64),
+ "u128" => UintSimplifiedType(UintTy::U128),
+ "f32" => FloatSimplifiedType(FloatTy::F32),
+ "f64" => FloatSimplifiedType(FloatTy::F64),
+ _ => return [].iter().copied(),
+ };
+
+ tcx.incoherent_impls(ty).iter().copied()
+}
+
+fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
+ match tcx.def_kind(def_id) {
+ DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
+ .module_children(def_id)
+ .iter()
+ .filter(|item| item.ident.name == name)
+ .map(|child| child.res.expect_non_local())
+ .collect(),
+ DefKind::Impl => tcx
+ .associated_item_def_ids(def_id)
+ .iter()
+ .copied()
+ .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name)
+ .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id))
+ .collect(),
+ _ => Vec::new(),
+ }
+}
+
+fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> {
+ let hir = tcx.hir();
+
+ let root_mod;
+ let item_kind = match hir.find_by_def_id(local_id) {
+ Some(Node::Crate(r#mod)) => {
+ root_mod = ItemKind::Mod(r#mod);
+ &root_mod
+ },
+ Some(Node::Item(item)) => &item.kind,
+ _ => return Vec::new(),
+ };
+
+ let res = |ident: Ident, owner_id: OwnerId| {
+ if ident.name == name {
+ let def_id = owner_id.to_def_id();
+ Some(Res::Def(tcx.def_kind(def_id), def_id))
+ } else {
+ None
}
+ };
+
+ match item_kind {
+ ItemKind::Mod(r#mod) => r#mod
+ .item_ids
+ .iter()
+ .filter_map(|&item_id| res(hir.item(item_id).ident, item_id.owner_id))
+ .collect(),
+ ItemKind::Impl(r#impl) => r#impl
+ .items
+ .iter()
+ .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id))
+ .collect(),
+ ItemKind::Trait(.., trait_item_refs) => trait_item_refs
+ .iter()
+ .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id))
+ .collect(),
+ _ => Vec::new(),
}
+}
- fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> {
+fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
+ if let Some(local_id) = def_id.as_local() {
+ local_item_children_by_name(tcx, local_id, name)
+ } else {
+ non_local_item_children_by_name(tcx, def_id, name)
+ }
+}
+
+/// Resolves a def path like `std::vec::Vec`.
+///
+/// Can return multiple resolutions when there are multiple versions of the same crate, e.g.
+/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0.
+///
+/// Also returns multiple results when there are mulitple paths under the same name e.g. `std::vec`
+/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`].
+///
+/// This function is expensive and should be used sparingly.
+pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Vec<Res> {
+ fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> impl Iterator<Item = DefId> + '_ {
tcx.crates(())
.iter()
.copied()
- .find(|&num| tcx.crate_name(num).as_str() == name)
+ .filter(move |&num| tcx.crate_name(num) == name)
.map(CrateNum::as_def_id)
}
- let (base, path) = match *path {
+ let tcx = cx.tcx;
+
+ let (base, mut path) = match *path {
[primitive] => {
- return PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy);
+ return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
},
[base, ref path @ ..] => (base, path),
- _ => return Res::Err,
+ _ => return Vec::new(),
};
- let tcx = cx.tcx;
- let starts = find_primitive(tcx, base)
- .chain(find_crate(tcx, base))
+
+ let base_sym = Symbol::intern(base);
+
+ let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym {
+ Some(LOCAL_CRATE.as_def_id())
+ } else {
+ None
+ };
+
+ let starts = find_primitive_impls(tcx, base)
+ .chain(find_crates(tcx, base_sym))
+ .chain(local_crate)
.map(|id| Res::Def(tcx.def_kind(id), id));
- for first in starts {
- let last = path
- .iter()
- .copied()
- .enumerate()
- // for each segment, find the child item
- .try_fold(first, |res, (idx, segment)| {
- let matches_ns = |res: Res| {
- // If at the last segment in the path, respect the namespace hint
- if idx == path.len() - 1 {
- match namespace_hint {
- Some(ns) => res.matches_ns(ns),
- None => true,
- }
- } else {
- res.matches_ns(Namespace::TypeNS)
- }
- };
-
- let def_id = res.def_id();
- if let Some(item) = item_child_by_name(tcx, def_id, segment, matches_ns) {
- Some(item)
- } else if matches!(res, Res::Def(DefKind::Enum | DefKind::Struct, _)) {
- // it is not a child item so check inherent impl items
- tcx.inherent_impls(def_id)
- .iter()
- .find_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, segment, matches_ns))
- } else {
- None
- }
- });
+ let mut resolutions: Vec<Res> = starts.collect();
- if let Some(last) = last {
- return last;
- }
+ while let [segment, rest @ ..] = path {
+ path = rest;
+ let segment = Symbol::intern(segment);
+
+ resolutions = resolutions
+ .into_iter()
+ .filter_map(|res| res.opt_def_id())
+ .flat_map(|def_id| {
+ // When the current def_id is e.g. `struct S`, check the impl items in
+ // `impl S { ... }`
+ let inherent_impl_children = tcx
+ .inherent_impls(def_id)
+ .iter()
+ .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment));
+
+ let direct_children = item_children_by_name(tcx, def_id, segment);
+
+ inherent_impl_children.chain(direct_children)
+ })
+ .collect();
}
- Res::Err
+ resolutions
+}
+
+/// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`].
+pub fn def_path_def_ids(cx: &LateContext<'_>, path: &[&str]) -> impl Iterator<Item = DefId> {
+ def_path_res(cx, path).into_iter().filter_map(|res| res.opt_def_id())
}
/// Convenience function to get the `DefId` of a trait by path.
@@ -651,10 +686,10 @@ pub fn def_path_res(cx: &LateContext<'_>, path: &[&str], namespace_hint: Option<
///
/// This function is expensive and should be used sparingly.
pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
- match def_path_res(cx, path, Some(Namespace::TypeNS)) {
+ def_path_res(cx, path).into_iter().find_map(|res| match res {
Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
_ => None,
- }
+ })
}
/// Gets the `hir::TraitRef` of the trait the given method is implemented for.
@@ -760,7 +795,6 @@ pub fn can_mut_borrow_both(cx: &LateContext<'_>, e1: &Expr<'_>, e2: &Expr<'_>) -
/// constructor from the std library
fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
let std_types_symbols = &[
- sym::String,
sym::Vec,
sym::VecDeque,
sym::LinkedList,
@@ -775,9 +809,9 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<
if method.ident.name == sym::new {
if let Some(impl_did) = cx.tcx.impl_of_method(def_id) {
if let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def() {
- return std_types_symbols
- .iter()
- .any(|&symbol| cx.tcx.is_diagnostic_item(symbol, adt.did()));
+ return std_types_symbols.iter().any(|&symbol| {
+ cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
+ });
}
}
}
@@ -834,7 +868,7 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &
ExprKind::Lit(hir::Lit {
node: LitKind::Str(ref sym, _),
..
- }) => return sym.is_empty() && is_path_diagnostic_item(cx, ty, sym::String),
+ }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
ExprKind::Repeat(_, ArrayLen::Body(len)) => {
if let ExprKind::Lit(ref const_lit) = cx.tcx.hir().body(len.body).value.kind &&
@@ -954,7 +988,7 @@ impl std::ops::BitOrAssign for CaptureKind {
/// Note as this will walk up to parent expressions until the capture can be determined it should
/// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
/// function argument (other than a receiver).
-pub fn capture_local_usage<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> CaptureKind {
+pub fn capture_local_usage(cx: &LateContext<'_>, e: &Expr<'_>) -> CaptureKind {
fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
let mut capture = CaptureKind::Ref(Mutability::Not);
pat.each_binding_or_first(&mut |_, id, span, _| match cx
@@ -1251,23 +1285,6 @@ pub fn contains_return(expr: &hir::Expr<'_>) -> bool {
.is_some()
}
-/// Extends the span to the beginning of the spans line, incl. whitespaces.
-///
-/// ```rust
-/// let x = ();
-/// // ^^
-/// // will be converted to
-/// let x = ();
-/// // ^^^^^^^^^^^^^^
-/// ```
-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]);
- span.with_lo(line_start)
-}
-
/// Gets the parent node, if any.
pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
tcx.hir().parent_iter(id).next().map(|(_, node)| node)
@@ -1740,6 +1757,10 @@ pub fn has_attr(attrs: &[ast::Attribute], symbol: Symbol) -> bool {
attrs.iter().any(|attr| attr.has_name(symbol))
}
+pub fn has_repr_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
+ has_attr(cx.tcx.hir().attrs(hir_id), sym::repr)
+}
+
pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool {
let map = &tcx.hir();
let mut prev_enclosing_node = None;
@@ -1812,7 +1833,7 @@ pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]])
}
/// Checks if the given `DefId` matches the path.
-pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
+pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool {
// We should probably move to Symbols in Clippy as well rather than interning every time.
let path = cx.get_def_path(did);
syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
@@ -1861,7 +1882,11 @@ 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 {
- matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness == IsAsync::Async)
+ match kind {
+ FnKind::ItemFn(_, _, header) => header.asyncness == IsAsync::Async,
+ FnKind::Method(_, sig) => sig.header.asyncness == IsAsync::Async,
+ FnKind::Closure => false,
+ }
}
/// Peels away all the compiler generated code surrounding the body of an async function,
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index 9a682fbe6..d13b34a66 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -199,12 +199,12 @@ pub fn first_node_in_macro(cx: &LateContext<'_>, node: &impl HirNode) -> Option<
pub fn is_panic(cx: &LateContext<'_>, def_id: DefId) -> bool {
let Some(name) = cx.tcx.get_diagnostic_name(def_id) else { return false };
matches!(
- name.as_str(),
- "core_panic_macro"
- | "std_panic_macro"
- | "core_panic_2015_macro"
- | "std_panic_2015_macro"
- | "core_panic_2021_macro"
+ name,
+ sym::core_panic_macro
+ | sym::std_panic_macro
+ | sym::core_panic_2015_macro
+ | sym::std_panic_2015_macro
+ | sym::core_panic_2021_macro
)
}
diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs
index 8b843732a..12a512f78 100644
--- a/src/tools/clippy/clippy_utils/src/msrvs.rs
+++ b/src/tools/clippy/clippy_utils/src/msrvs.rs
@@ -1,4 +1,11 @@
+use std::sync::OnceLock;
+
+use rustc_ast::Attribute;
use rustc_semver::RustcVersion;
+use rustc_session::Session;
+use rustc_span::Span;
+
+use crate::attrs::get_unique_attr;
macro_rules! msrv_aliases {
($($major:literal,$minor:literal,$patch:literal {
@@ -12,13 +19,14 @@ macro_rules! msrv_aliases {
// names may refer to stabilized feature flags or library items
msrv_aliases! {
+ 1,65,0 { LET_ELSE }
1,62,0 { BOOL_THEN_SOME }
1,58,0 { FORMAT_ARGS_CAPTURE }
1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
- 1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS }
+ 1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
1,50,0 { BOOL_THEN, CLAMP }
- 1,47,0 { TAU }
+ 1,47,0 { TAU, IS_ASCII_DIGIT_CONST }
1,46,0 { CONST_IF_MATCH }
1,45,0 { STR_STRIP_PREFIX }
1,43,0 { LOG2_10, LOG10_2 }
@@ -37,4 +45,99 @@ msrv_aliases! {
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,55,0 { SEEK_REWIND }
+}
+
+fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
+ if let Ok(version) = RustcVersion::parse(msrv) {
+ return Some(version);
+ } else if let Some(sess) = sess {
+ if let Some(span) = span {
+ sess.span_err(span, format!("`{msrv}` is not a valid Rust version"));
+ }
+ }
+ None
+}
+
+/// Tracks the current MSRV from `clippy.toml`, `Cargo.toml` or set via `#[clippy::msrv]`
+#[derive(Debug, Clone, Default)]
+pub struct Msrv {
+ stack: Vec<RustcVersion>,
+}
+
+impl Msrv {
+ fn new(initial: Option<RustcVersion>) -> Self {
+ Self {
+ stack: Vec::from_iter(initial),
+ }
+ }
+
+ fn read_inner(conf_msrv: &Option<String>, sess: &Session) -> Self {
+ let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
+ .ok()
+ .and_then(|v| parse_msrv(&v, None, None));
+ let clippy_msrv = conf_msrv.as_ref().and_then(|s| {
+ parse_msrv(s, None, None).or_else(|| {
+ sess.err(format!(
+ "error reading Clippy's configuration file. `{s}` is not a valid Rust version"
+ ));
+ None
+ })
+ });
+
+ // if both files have an msrv, let's compare them and emit a warning if they differ
+ if let Some(cargo_msrv) = cargo_msrv
+ && let Some(clippy_msrv) = clippy_msrv
+ && clippy_msrv != cargo_msrv
+ {
+ sess.warn(format!(
+ "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`"
+ ));
+ }
+
+ Self::new(clippy_msrv.or(cargo_msrv))
+ }
+
+ /// Set the initial MSRV from the Clippy config file or from Cargo due to the `rust-version`
+ /// field in `Cargo.toml`
+ ///
+ /// Returns a `&'static Msrv` as `Copy` types are more easily passed to the
+ /// `register_{late,early}_pass` callbacks
+ pub fn read(conf_msrv: &Option<String>, sess: &Session) -> &'static Self {
+ static PARSED: OnceLock<Msrv> = OnceLock::new();
+
+ PARSED.get_or_init(|| Self::read_inner(conf_msrv, sess))
+ }
+
+ pub fn current(&self) -> Option<RustcVersion> {
+ self.stack.last().copied()
+ }
+
+ pub fn meets(&self, required: RustcVersion) -> bool {
+ self.current().map_or(true, |version| version.meets(required))
+ }
+
+ fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option<RustcVersion> {
+ if let Some(msrv_attr) = get_unique_attr(sess, attrs, "msrv") {
+ if let Some(msrv) = msrv_attr.value_str() {
+ return parse_msrv(&msrv.to_string(), Some(sess), Some(msrv_attr.span));
+ }
+
+ sess.span_err(msrv_attr.span, "bad clippy attribute");
+ }
+
+ None
+ }
+
+ pub fn enter_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) {
+ if let Some(version) = Self::parse_attr(sess, attrs) {
+ self.stack.push(version);
+ }
+ }
+
+ pub fn exit_lint_attrs(&mut self, sess: &Session, attrs: &[Attribute]) {
+ if Self::parse_attr(sess, attrs).is_some() {
+ self.stack.pop();
+ }
+ }
}
diff --git a/src/tools/clippy/clippy_utils/src/numeric_literal.rs b/src/tools/clippy/clippy_utils/src/numeric_literal.rs
index c5dcd7b31..42bdfd482 100644
--- a/src/tools/clippy/clippy_utils/src/numeric_literal.rs
+++ b/src/tools/clippy/clippy_utils/src/numeric_literal.rs
@@ -1,4 +1,4 @@
-use rustc_ast::ast::{Lit, LitFloatType, LitIntType, LitKind};
+use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use std::iter;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
@@ -46,10 +46,6 @@ pub struct NumericLiteral<'a> {
}
impl<'a> NumericLiteral<'a> {
- pub fn from_lit(src: &'a str, lit: &Lit) -> Option<NumericLiteral<'a>> {
- NumericLiteral::from_lit_kind(src, &lit.kind)
- }
-
pub fn from_lit_kind(src: &'a str, lit_kind: &LitKind) -> Option<NumericLiteral<'a>> {
let unsigned_src = src.strip_prefix('-').map_or(src, |s| s);
if lit_kind.is_numeric()
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index bc8514734..6417f0f3c 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -60,6 +60,8 @@ pub const LATE_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "LateLintPass"];
#[cfg(feature = "internal")]
pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
pub const MEM_SWAP: [&str; 3] = ["core", "mem", "swap"];
+#[cfg(feature = "internal")]
+pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"];
pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
@@ -72,7 +74,6 @@ pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekab
pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
#[cfg_attr(not(unix), allow(clippy::invalid_paths))]
pub const PERMISSIONS_FROM_MODE: [&str; 6] = ["std", "os", "unix", "fs", "PermissionsExt", "from_mode"];
-pub const POLL: [&str; 4] = ["core", "task", "poll", "Poll"];
pub const PTR_COPY: [&str; 3] = ["core", "intrinsics", "copy"];
pub const PTR_COPY_NONOVERLAPPING: [&str; 3] = ["core", "intrinsics", "copy_nonoverlapping"];
pub const PTR_EQ: [&str; 3] = ["core", "ptr", "eq"];
@@ -101,8 +102,6 @@ pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
-#[cfg(feature = "internal")]
-pub const RUSTC_VERSION: [&str; 2] = ["rustc_semver", "RustcVersion"];
pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_parts"];
@@ -115,6 +114,9 @@ pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
+pub const STD_IO_SEEK: [&str; 3] = ["std", "io", "Seek"];
+pub const STD_IO_SEEK_FROM_CURRENT: [&str; 4] = ["std", "io", "SeekFrom", "Current"];
+pub const STD_IO_SEEKFROM_START: [&str; 4] = ["std", "io", "SeekFrom", "Start"];
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
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 45b63a4aa..480e8e55c 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
@@ -3,6 +3,7 @@
// of terminologies might not be relevant in the context of Clippy. Note that its behavior might
// differ from the time of `rustc` even if the name stays the same.
+use crate::msrvs::Msrv;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::mir::{
@@ -18,25 +19,28 @@ use std::borrow::Cow;
type McfResult = Result<(), (Span, Cow<'static, str>)>;
-pub fn is_min_const_fn<'a, 'tcx>(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, msrv: Option<RustcVersion>) -> McfResult {
+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::RegionOutlives(_)
- | ty::PredicateKind::TypeOutlives(_)
+ ty::PredicateKind::Clause(
+ ty::Clause::RegionOutlives(_)
+ | ty::Clause::TypeOutlives(_)
+ | ty::Clause::Projection(_)
+ | ty::Clause::Trait(..),
+ )
| ty::PredicateKind::WellFormed(_)
- | ty::PredicateKind::Projection(_)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..)
- | ty::PredicateKind::Trait(..)
| ty::PredicateKind::TypeWellFormedFromEnv(..) => continue,
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 {
@@ -276,11 +280,11 @@ fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &B
Ok(())
}
-fn check_terminator<'a, 'tcx>(
+fn check_terminator<'tcx>(
tcx: TyCtxt<'tcx>,
- body: &'a Body<'tcx>,
+ body: &Body<'tcx>,
terminator: &Terminator<'tcx>,
- msrv: Option<RustcVersion>,
+ msrv: &Msrv,
) -> McfResult {
let span = terminator.source_info.span;
match &terminator.kind {
@@ -364,7 +368,7 @@ fn check_terminator<'a, 'tcx>(
}
}
-fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bool {
+fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
tcx.is_const_fn(def_id)
&& tcx.lookup_const_stability(def_id).map_or(true, |const_stab| {
if let rustc_attr::StabilityLevel::Stable { since, .. } = const_stab.level {
@@ -383,15 +387,12 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: Option<RustcVersion>) -> bo
let since = rustc_span::Symbol::intern(short_version);
- crate::meets_msrv(
- msrv,
- RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
- panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
- }),
- )
+ msrv.meets(RustcVersion::parse(since.as_str()).unwrap_or_else(|err| {
+ panic!("`rustc_attr::StabilityLevel::Stable::since` is ill-formatted: `{since}`, {err:?}")
+ }))
} else {
// Unstable const fn with the feature enabled.
- msrv.is_none()
+ msrv.current().is_none()
}
})
}
diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs
index d28bd92d7..cd5dcfdac 100644
--- a/src/tools/clippy/clippy_utils/src/source.rs
+++ b/src/tools/clippy/clippy_utils/src/source.rs
@@ -2,13 +2,13 @@
#![allow(clippy::module_name_repetitions)]
-use crate::line_span;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LintContext};
+use rustc_session::Session;
use rustc_span::hygiene;
-use rustc_span::source_map::SourceMap;
-use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext};
+use rustc_span::source_map::{original_sp, SourceMap};
+use rustc_span::{BytePos, Pos, Span, SpanData, SyntaxContext, DUMMY_SP};
use std::borrow::Cow;
/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
@@ -55,6 +55,23 @@ fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePo
})
}
+/// Extends the span to the beginning of the spans line, incl. whitespaces.
+///
+/// ```rust
+/// let x = ();
+/// // ^^
+/// // will be converted to
+/// let x = ();
+/// // ^^^^^^^^^^^^^^
+/// ```
+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]);
+ span.with_lo(line_start)
+}
+
/// Returns the indentation of the line of a span
///
/// ```rust,ignore
@@ -189,10 +206,19 @@ pub fn snippet_with_applicability<'a, T: LintContext>(
default: &'a str,
applicability: &mut Applicability,
) -> Cow<'a, str> {
+ snippet_with_applicability_sess(cx.sess(), span, default, applicability)
+}
+
+fn snippet_with_applicability_sess<'a>(
+ sess: &Session,
+ span: Span,
+ default: &'a str,
+ applicability: &mut Applicability,
+) -> Cow<'a, str> {
if *applicability != Applicability::Unspecified && span.from_expansion() {
*applicability = Applicability::MaybeIncorrect;
}
- snippet_opt(cx, span).map_or_else(
+ snippet_opt_sess(sess, span).map_or_else(
|| {
if *applicability == Applicability::MachineApplicable {
*applicability = Applicability::HasPlaceholders;
@@ -210,8 +236,12 @@ pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, defau
}
/// Converts a span to a code snippet. Returns `None` if not available.
-pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
- cx.sess().source_map().span_to_snippet(span).ok()
+pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> {
+ snippet_opt_sess(cx.sess(), span)
+}
+
+fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> {
+ sess.source_map().span_to_snippet(span).ok()
}
/// Converts a span (from a block) to a code snippet if available, otherwise use default.
@@ -261,8 +291,8 @@ pub fn snippet_block<'a, T: LintContext>(
/// Same as `snippet_block`, but adapts the applicability level by the rules of
/// `snippet_with_applicability`.
-pub fn snippet_block_with_applicability<'a, T: LintContext>(
- cx: &T,
+pub fn snippet_block_with_applicability<'a>(
+ cx: &impl LintContext,
span: Span,
default: &'a str,
indent_relative_to: Option<Span>,
@@ -283,7 +313,17 @@ pub fn snippet_block_with_applicability<'a, T: LintContext>(
///
/// This will also return whether or not the snippet is a macro call.
pub fn snippet_with_context<'a>(
- cx: &LateContext<'_>,
+ cx: &impl LintContext,
+ span: Span,
+ outer: SyntaxContext,
+ default: &'a str,
+ applicability: &mut Applicability,
+) -> (Cow<'a, str>, bool) {
+ snippet_with_context_sess(cx.sess(), span, outer, default, applicability)
+}
+
+fn snippet_with_context_sess<'a>(
+ sess: &Session,
span: Span,
outer: SyntaxContext,
default: &'a str,
@@ -302,7 +342,7 @@ pub fn snippet_with_context<'a>(
);
(
- snippet_with_applicability(cx, span, default, applicability),
+ snippet_with_applicability_sess(sess, span, default, applicability),
is_macro_call,
)
}
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index aad7da61a..b66604f33 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -176,25 +176,28 @@ impl<'a> Sugg<'a> {
}
/// Prepare a suggestion from an expression.
- pub fn ast(cx: &EarlyContext<'_>, expr: &ast::Expr, default: &'a str) -> Self {
+ pub fn ast(
+ cx: &EarlyContext<'_>,
+ expr: &ast::Expr,
+ default: &'a str,
+ ctxt: SyntaxContext,
+ app: &mut Applicability,
+ ) -> Self {
use rustc_ast::ast::RangeLimits;
- let snippet_without_expansion = |cx, span: Span, default| {
- if span.from_expansion() {
- snippet_with_macro_callsite(cx, span, default)
- } else {
- snippet(cx, span, default)
- }
- };
-
+ #[expect(clippy::match_wildcard_for_single_variants)]
match expr.kind {
+ _ if expr.span.ctxt() != ctxt => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
ast::ExprKind::AddrOf(..)
| ast::ExprKind::Box(..)
| ast::ExprKind::Closure { .. }
| ast::ExprKind::If(..)
| ast::ExprKind::Let(..)
| ast::ExprKind::Unary(..)
- | ast::ExprKind::Match(..) => Sugg::MaybeParen(snippet_without_expansion(cx, expr.span, default)),
+ | ast::ExprKind::Match(..) => match snippet_with_context(cx, expr.span, ctxt, default, app) {
+ (snip, false) => Sugg::MaybeParen(snip),
+ (snip, true) => Sugg::NonParen(snip),
+ },
ast::ExprKind::Async(..)
| ast::ExprKind::Block(..)
| ast::ExprKind::Break(..)
@@ -207,6 +210,7 @@ impl<'a> Sugg<'a> {
| ast::ExprKind::InlineAsm(..)
| ast::ExprKind::ConstBlock(..)
| ast::ExprKind::Lit(..)
+ | ast::ExprKind::IncludedBytes(..)
| ast::ExprKind::Loop(..)
| ast::ExprKind::MacCall(..)
| ast::ExprKind::MethodCall(..)
@@ -223,45 +227,49 @@ impl<'a> Sugg<'a> {
| ast::ExprKind::Array(..)
| ast::ExprKind::While(..)
| ast::ExprKind::Await(..)
- | ast::ExprKind::Err => Sugg::NonParen(snippet_without_expansion(cx, expr.span, default)),
+ | ast::ExprKind::Err => Sugg::NonParen(snippet_with_context(cx, expr.span, ctxt, default, app).0),
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::HalfOpen) => Sugg::BinOp(
AssocOp::DotDot,
- lhs.as_ref()
- .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
- rhs.as_ref()
- .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
+ lhs.as_ref().map_or("".into(), |lhs| {
+ snippet_with_context(cx, lhs.span, ctxt, default, app).0
+ }),
+ rhs.as_ref().map_or("".into(), |rhs| {
+ snippet_with_context(cx, rhs.span, ctxt, default, app).0
+ }),
),
ast::ExprKind::Range(ref lhs, ref rhs, RangeLimits::Closed) => Sugg::BinOp(
AssocOp::DotDotEq,
- lhs.as_ref()
- .map_or("".into(), |lhs| snippet_without_expansion(cx, lhs.span, default)),
- rhs.as_ref()
- .map_or("".into(), |rhs| snippet_without_expansion(cx, rhs.span, default)),
+ lhs.as_ref().map_or("".into(), |lhs| {
+ snippet_with_context(cx, lhs.span, ctxt, default, app).0
+ }),
+ rhs.as_ref().map_or("".into(), |rhs| {
+ snippet_with_context(cx, rhs.span, ctxt, default, app).0
+ }),
),
ast::ExprKind::Assign(ref lhs, ref rhs, _) => Sugg::BinOp(
AssocOp::Assign,
- snippet_without_expansion(cx, lhs.span, default),
- snippet_without_expansion(cx, rhs.span, default),
+ snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+ snippet_with_context(cx, rhs.span, ctxt, default, app).0,
),
ast::ExprKind::AssignOp(op, ref lhs, ref rhs) => Sugg::BinOp(
astbinop2assignop(op),
- snippet_without_expansion(cx, lhs.span, default),
- snippet_without_expansion(cx, rhs.span, default),
+ snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+ snippet_with_context(cx, rhs.span, ctxt, default, app).0,
),
ast::ExprKind::Binary(op, ref lhs, ref rhs) => Sugg::BinOp(
AssocOp::from_ast_binop(op.node),
- snippet_without_expansion(cx, lhs.span, default),
- snippet_without_expansion(cx, rhs.span, default),
+ snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+ snippet_with_context(cx, rhs.span, ctxt, default, app).0,
),
ast::ExprKind::Cast(ref lhs, ref ty) => Sugg::BinOp(
AssocOp::As,
- snippet_without_expansion(cx, lhs.span, default),
- snippet_without_expansion(cx, ty.span, default),
+ snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+ snippet_with_context(cx, ty.span, ctxt, default, app).0,
),
ast::ExprKind::Type(ref lhs, ref ty) => Sugg::BinOp(
AssocOp::Colon,
- snippet_without_expansion(cx, lhs.span, default),
- snippet_without_expansion(cx, ty.span, default),
+ snippet_with_context(cx, lhs.span, ctxt, default, app).0,
+ snippet_with_context(cx, ty.span, ctxt, default, app).0,
),
}
}
@@ -800,7 +808,7 @@ pub struct DerefClosure {
/// Returns `None` if no such use cases have been triggered in closure body
///
/// note: this only works on single line immutable closures with exactly one input parameter.
-pub fn deref_closure_args<'tcx>(cx: &LateContext<'_>, closure: &'tcx hir::Expr<'_>) -> Option<DerefClosure> {
+pub fn deref_closure_args(cx: &LateContext<'_>, closure: &hir::Expr<'_>) -> Option<DerefClosure> {
if let hir::ExprKind::Closure(&Closure { fn_decl, body, .. }) = closure.kind {
let closure_body = cx.tcx.hir().body(body);
// is closure arg a type annotated double reference (i.e.: `|x: &&i32| ...`)
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 4e024ce40..05c6eb7f2 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -9,19 +9,23 @@ use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
-use rustc_infer::infer::TyCtxtInferExt;
+use rustc_infer::infer::{
+ type_variable::{TypeVariableOrigin, TypeVariableOriginKind},
+ TyCtxtInferExt,
+};
use rustc_lint::LateContext;
use rustc_middle::mir::interpret::{ConstValue, Scalar};
use rustc_middle::ty::{
- self, AdtDef, Binder, BoundRegion, DefIdTree, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy,
- Region, RegionKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy, VariantDef, VariantDiscr,
+ self, AdtDef, AssocKind, Binder, BoundRegion, DefIdTree, FnSig, IntTy, List, ParamEnv, Predicate, PredicateKind,
+ ProjectionTy, Region, RegionKind, SubstsRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, UintTy,
+ VariantDef, VariantDiscr,
};
use rustc_middle::ty::{GenericArg, GenericArgKind};
use rustc_span::symbol::Ident;
-use rustc_span::{sym, Span, Symbol};
+use rustc_span::{sym, Span, Symbol, DUMMY_SP};
use rustc_target::abi::{Size, VariantIdx};
use rustc_trait_selection::infer::InferCtxtExt;
-use rustc_trait_selection::traits::query::normalize::AtExt;
+use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
use std::iter;
use crate::{match_def_path, path_res, paths};
@@ -59,29 +63,80 @@ pub fn contains_adt_constructor<'tcx>(ty: Ty<'tcx>, adt: AdtDef<'tcx>) -> bool {
})
}
+/// Walks into `ty` and returns `true` if any inner type is an instance of the given type, or adt
+/// constructor of the same type.
+///
+/// This method also recurses into opaque type predicates, so call it with `impl Trait<U>` and `U`
+/// will also return `true`.
+pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, needle: Ty<'tcx>) -> bool {
+ fn contains_ty_adt_constructor_opaque_inner<'tcx>(
+ cx: &LateContext<'tcx>,
+ ty: Ty<'tcx>,
+ needle: Ty<'tcx>,
+ seen: &mut FxHashSet<DefId>,
+ ) -> bool {
+ ty.walk().any(|inner| match inner.unpack() {
+ GenericArgKind::Type(inner_ty) => {
+ if inner_ty == needle {
+ return true;
+ }
+
+ if inner_ty.ty_adt_def() == needle.ty_adt_def() {
+ return true;
+ }
+
+ if let ty::Opaque(def_id, _) = *inner_ty.kind() {
+ if !seen.insert(def_id) {
+ return false;
+ }
+
+ for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
+ match predicate.kind().skip_binder() {
+ // For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through
+ // and check substituions to find `U`.
+ ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) => {
+ if trait_predicate
+ .trait_ref
+ .substs
+ .types()
+ .skip(1) // Skip the implicit `Self` generic parameter
+ .any(|ty| contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen))
+ {
+ return true;
+ }
+ },
+ // 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)) => {
+ if let ty::TermKind::Ty(ty) = projection_predicate.term.unpack() {
+ if contains_ty_adt_constructor_opaque_inner(cx, ty, needle, seen) {
+ return true;
+ }
+ };
+ },
+ _ => (),
+ }
+ }
+ }
+
+ false
+ },
+ GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
+ })
+ }
+
+ // A hash set to ensure that the same opaque type (`impl Trait` in RPIT or TAIT) is not
+ // visited twice.
+ let mut seen = FxHashSet::default();
+ contains_ty_adt_constructor_opaque_inner(cx, ty, needle, &mut seen)
+}
+
/// Resolves `<T as Iterator>::Item` for `T`
/// Do not invoke without first verifying that the type implements `Iterator`
pub fn get_iterator_item_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
cx.tcx
.get_diagnostic_item(sym::Iterator)
- .and_then(|iter_did| get_associated_type(cx, ty, iter_did, "Item"))
-}
-
-/// Returns the associated type `name` for `ty` as an implementation of `trait_id`.
-/// Do not invoke without first verifying that the type implements the trait.
-pub fn get_associated_type<'tcx>(
- cx: &LateContext<'tcx>,
- ty: Ty<'tcx>,
- trait_id: DefId,
- name: &str,
-) -> Option<Ty<'tcx>> {
- cx.tcx
- .associated_items(trait_id)
- .find_by_name_and_kind(cx.tcx, Ident::from_str(name), ty::AssocKind::Type, trait_id)
- .and_then(|assoc| {
- let proj = cx.tcx.mk_projection(assoc.def_id, cx.tcx.mk_substs_trait(ty, &[]));
- cx.tcx.try_normalize_erasing_regions(cx.param_env, proj).ok()
- })
+ .and_then(|iter_did| cx.get_associated_type(ty, iter_did, "Item"))
}
/// Get the diagnostic name of a type, e.g. `sym::HashMap`. To check if a type
@@ -153,7 +208,13 @@ pub fn implements_trait<'tcx>(
trait_id: DefId,
ty_params: &[GenericArg<'tcx>],
) -> bool {
- implements_trait_with_env(cx.tcx, cx.param_env, ty, trait_id, ty_params)
+ implements_trait_with_env(
+ cx.tcx,
+ cx.param_env,
+ ty,
+ trait_id,
+ ty_params.iter().map(|&arg| Some(arg)),
+ )
}
/// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
@@ -162,7 +223,7 @@ pub fn implements_trait_with_env<'tcx>(
param_env: ParamEnv<'tcx>,
ty: Ty<'tcx>,
trait_id: DefId,
- ty_params: &[GenericArg<'tcx>],
+ ty_params: impl IntoIterator<Item = Option<GenericArg<'tcx>>>,
) -> bool {
// Clippy shouldn't have infer types
assert!(!ty.needs_infer());
@@ -171,10 +232,18 @@ pub fn implements_trait_with_env<'tcx>(
if ty.has_escaping_bound_vars() {
return false;
}
- let ty_params = tcx.mk_substs(ty_params.iter());
let infcx = tcx.infer_ctxt().build();
+ let orig = TypeVariableOrigin {
+ kind: TypeVariableOriginKind::MiscVariable,
+ span: DUMMY_SP,
+ };
+ let ty_params = tcx.mk_substs(
+ ty_params
+ .into_iter()
+ .map(|arg| arg.unwrap_or_else(|| infcx.next_ty_var(orig).into())),
+ );
infcx
- .type_implements_trait(trait_id, ty, ty_params, param_env)
+ .type_implements_trait(trait_id, [ty.into()].into_iter().chain(ty_params), param_env)
.must_apply_modulo_regions()
}
@@ -199,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::Opaque(def_id, _) => {
for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
- if let ty::PredicateKind::Trait(trait_predicate) = predicate.kind().skip_binder() {
+ if let ty::PredicateKind::Clause(ty::Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() {
if cx.tcx.has_attr(trait_predicate.trait_ref.def_id, sym::must_use) {
return true;
}
@@ -243,7 +312,7 @@ fn is_normalizable_helper<'tcx>(
cache.insert(ty, false);
let infcx = cx.tcx.infer_ctxt().build();
let cause = rustc_middle::traits::ObligationCause::dummy();
- let result = if infcx.at(&cause, param_env).normalize(ty).is_ok() {
+ let result = if infcx.at(&cause, param_env).query_normalize(ty).is_ok() {
match ty.kind() {
ty::Adt(def, substs) => def.variants().iter().all(|variant| {
variant
@@ -318,11 +387,7 @@ pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symb
/// Returns `false` if the `LangItem` is not defined.
pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
match ty.kind() {
- ty::Adt(adt, _) => cx
- .tcx
- .lang_items()
- .require(lang_item)
- .map_or(false, |li| li == adt.did()),
+ ty::Adt(adt, _) => cx.tcx.lang_items().get(lang_item) == Some(adt.did()),
_ => false,
}
}
@@ -622,7 +687,7 @@ fn sig_from_bounds<'tcx>(
for pred in predicates {
match pred.kind().skip_binder() {
- PredicateKind::Trait(p)
+ PredicateKind::Clause(ty::Clause::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()))
@@ -635,7 +700,7 @@ fn sig_from_bounds<'tcx>(
}
inputs = Some(i);
},
- PredicateKind::Projection(p)
+ PredicateKind::Clause(ty::Clause::Projection(p))
if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
&& p.projection_ty.self_ty() == ty =>
{
@@ -663,7 +728,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O
.subst_iter_copied(cx.tcx, ty.substs)
{
match pred.kind().skip_binder() {
- PredicateKind::Trait(p)
+ PredicateKind::Clause(ty::Clause::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())) =>
@@ -676,7 +741,9 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> O
}
inputs = Some(i);
},
- PredicateKind::Projection(p) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => {
+ PredicateKind::Clause(ty::Clause::Projection(p))
+ if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() =>
+ {
if output.is_some() {
// Multiple different fn trait impls. Is this even allowed?
return None;
@@ -705,7 +772,7 @@ impl core::ops::Add<u32> for EnumValue {
}
}
-/// Attempts to read the given constant as though it were an an enum value.
+/// Attempts to read the given constant as though it were an enum value.
#[expect(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
pub fn read_explicit_enum_value(tcx: TyCtxt<'_>, id: DefId) -> Option<EnumValue> {
if let Ok(ConstValue::Scalar(Scalar::Int(value))) = tcx.const_eval_poly(id) {
@@ -786,6 +853,42 @@ pub fn for_each_top_level_late_bound_region<B>(
ty.visit_with(&mut V { index: 0, f })
}
+pub struct AdtVariantInfo {
+ pub ind: usize,
+ pub size: u64,
+
+ /// (ind, size)
+ pub fields_size: Vec<(usize, u64)>,
+}
+
+impl AdtVariantInfo {
+ /// Returns ADT variants ordered by size
+ pub fn new<'tcx>(cx: &LateContext<'tcx>, adt: AdtDef<'tcx>, subst: &'tcx List<GenericArg<'tcx>>) -> Vec<Self> {
+ let mut variants_size = adt
+ .variants()
+ .iter()
+ .enumerate()
+ .map(|(i, variant)| {
+ let mut fields_size = variant
+ .fields
+ .iter()
+ .enumerate()
+ .map(|(i, f)| (i, approx_ty_size(cx, f.ty(cx.tcx, subst))))
+ .collect::<Vec<_>>();
+ fields_size.sort_by(|(_, a_size), (_, b_size)| (a_size.cmp(b_size)));
+
+ Self {
+ ind: i,
+ size: fields_size.iter().map(|(_, size)| size).sum(),
+ fields_size,
+ }
+ })
+ .collect::<Vec<_>>();
+ variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
+ variants_size
+ }
+}
+
/// Gets the struct or enum variant from the given `Res`
pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx VariantDef> {
match res {
@@ -815,7 +918,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::Trait(p) = p.kind().skip_binder()
+ if let PredicateKind::Clause(ty::Clause::Trait(p)) = p.kind().skip_binder()
&& let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
&& ty.index == self_ty.index
{
@@ -880,3 +983,127 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
(Err(_), _) => 0,
}
}
+
+/// Makes the projection type for the named associated type in the given impl or trait impl.
+///
+/// This function is for associated types which are "known" to exist, and as such, will only return
+/// `None` when debug assertions are disabled in order to prevent ICE's. With debug assertions
+/// enabled this will check that the named associated type exists, the correct number of
+/// substitutions are given, and that the correct kinds of substitutions are given (lifetime,
+/// constant or type). This will not check if type normalization would succeed.
+pub fn make_projection<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ container_id: DefId,
+ assoc_ty: Symbol,
+ substs: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
+) -> Option<ProjectionTy<'tcx>> {
+ fn helper<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ container_id: DefId,
+ assoc_ty: Symbol,
+ substs: SubstsRef<'tcx>,
+ ) -> Option<ProjectionTy<'tcx>> {
+ let Some(assoc_item) = tcx
+ .associated_items(container_id)
+ .find_by_name_and_kind(tcx, Ident::with_dummy_span(assoc_ty), AssocKind::Type, container_id)
+ else {
+ debug_assert!(false, "type `{assoc_ty}` not found in `{container_id:?}`");
+ return None;
+ };
+ #[cfg(debug_assertions)]
+ {
+ let generics = tcx.generics_of(assoc_item.def_id);
+ let generic_count = generics.parent_count + generics.params.len();
+ let params = generics
+ .parent
+ .map_or([].as_slice(), |id| &*tcx.generics_of(id).params)
+ .iter()
+ .chain(&generics.params)
+ .map(|x| &x.kind);
+
+ debug_assert!(
+ generic_count == substs.len(),
+ "wrong number of substs for `{:?}`: found `{}` expected `{generic_count}`.\n\
+ note: the expected parameters are: {:#?}\n\
+ the given arguments are: `{substs:#?}`",
+ assoc_item.def_id,
+ substs.len(),
+ params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>(),
+ );
+
+ if let Some((idx, (param, arg))) = params
+ .clone()
+ .zip(substs.iter().map(GenericArg::unpack))
+ .enumerate()
+ .find(|(_, (param, arg))| {
+ !matches!(
+ (param, arg),
+ (ty::GenericParamDefKind::Lifetime, GenericArgKind::Lifetime(_))
+ | (ty::GenericParamDefKind::Type { .. }, GenericArgKind::Type(_))
+ | (ty::GenericParamDefKind::Const { .. }, GenericArgKind::Const(_))
+ )
+ })
+ {
+ debug_assert!(
+ false,
+ "mismatched subst type at index {idx}: expected a {}, found `{arg:?}`\n\
+ note: the expected parameters are {:#?}\n\
+ the given arguments are {substs:#?}",
+ param.descr(),
+ params.map(ty::GenericParamDefKind::descr).collect::<Vec<_>>()
+ );
+ }
+ }
+
+ Some(ProjectionTy {
+ substs,
+ item_def_id: assoc_item.def_id,
+ })
+ }
+ helper(
+ tcx,
+ container_id,
+ assoc_ty,
+ tcx.mk_substs(substs.into_iter().map(Into::into)),
+ )
+}
+
+/// Normalizes the named associated type in the given impl or trait impl.
+///
+/// This function is for associated types which are "known" to be valid with the given
+/// substitutions, and as such, will only return `None` when debug assertions are disabled in order
+/// to prevent ICE's. With debug assertions enabled this will check that that type normalization
+/// succeeds as well as everything checked by `make_projection`.
+pub fn make_normalized_projection<'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: ProjectionTy<'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;
+ }
+ match tcx.try_normalize_erasing_regions(param_env, tcx.mk_projection(ty.item_def_id, ty.substs)) {
+ Ok(ty) => Some(ty),
+ Err(e) => {
+ debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}");
+ None
+ },
+ }
+ }
+ helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, substs)?)
+}
diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs
index 000fb51c0..ab3976a13 100644
--- a/src/tools/clippy/clippy_utils/src/usage.rs
+++ b/src/tools/clippy/clippy_utils/src/usage.rs
@@ -73,12 +73,7 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
self.update(cmt);
}
- fn fake_read(
- &mut self,
- _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>,
- _: FakeReadCause,
- _: HirId,
- ) {}
+ fn fake_read(&mut self, _: &rustc_hir_typeck::expr_use_visitor::PlaceWithHirId<'tcx>, _: FakeReadCause, _: HirId) {}
}
pub struct ParamBindingIdCollector {
@@ -133,7 +128,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for BindingUsageFinder<'a, 'tcx> {
}
}
- fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
+ fn visit_path(&mut self, path: &hir::Path<'tcx>, _: hir::HirId) {
if let hir::def::Res::Local(id) = path.res {
if self.binding_ids.contains(&id) {
self.usage_found = true;
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index d4294f18f..863fb60fc 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -170,22 +170,22 @@ where
cb: F,
}
- struct WithStmtGuarg<'a, F> {
+ struct WithStmtGuard<'a, F> {
val: &'a mut RetFinder<F>,
prev_in_stmt: bool,
}
impl<F> RetFinder<F> {
- fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuarg<'_, F> {
+ fn inside_stmt(&mut self, in_stmt: bool) -> WithStmtGuard<'_, F> {
let prev_in_stmt = std::mem::replace(&mut self.in_stmt, in_stmt);
- WithStmtGuarg {
+ WithStmtGuard {
val: self,
prev_in_stmt,
}
}
}
- impl<F> std::ops::Deref for WithStmtGuarg<'_, F> {
+ impl<F> std::ops::Deref for WithStmtGuard<'_, F> {
type Target = RetFinder<F>;
fn deref(&self) -> &Self::Target {
@@ -193,13 +193,13 @@ where
}
}
- impl<F> std::ops::DerefMut for WithStmtGuarg<'_, F> {
+ impl<F> std::ops::DerefMut for WithStmtGuard<'_, F> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.val
}
}
- impl<F> Drop for WithStmtGuarg<'_, F> {
+ impl<F> Drop for WithStmtGuard<'_, F> {
fn drop(&mut self) {
self.val.in_stmt = self.prev_in_stmt;
}