Merge remote-tracking branch 'upstream/master' into rustup
This commit is contained in:
commit
0bb1b5bd3b
263 changed files with 5325 additions and 4470 deletions
|
|
@ -468,7 +468,7 @@ declare_clippy_lint! {
|
|||
/// #[ignore = "Some good reason"]
|
||||
/// fn test() {}
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
#[clippy::version = "1.88.0"]
|
||||
pub IGNORE_WITHOUT_REASON,
|
||||
pedantic,
|
||||
"ignored tests without messages"
|
||||
|
|
|
|||
|
|
@ -26,16 +26,16 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) {
|
|||
|
||||
if namespace.is_none()
|
||||
&& matches!(
|
||||
name.as_str(),
|
||||
"ambiguous_glob_reexports"
|
||||
| "dead_code"
|
||||
| "deprecated"
|
||||
| "hidden_glob_reexports"
|
||||
| "unreachable_pub"
|
||||
| "unused"
|
||||
| "unused_braces"
|
||||
| "unused_import_braces"
|
||||
| "unused_imports"
|
||||
name,
|
||||
sym::ambiguous_glob_reexports
|
||||
| sym::dead_code
|
||||
| sym::deprecated
|
||||
| sym::hidden_glob_reexports
|
||||
| sym::unreachable_pub
|
||||
| sym::unused
|
||||
| sym::unused_braces
|
||||
| sym::unused_import_braces
|
||||
| sym::unused_imports
|
||||
)
|
||||
{
|
||||
return;
|
||||
|
|
@ -43,16 +43,16 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) {
|
|||
|
||||
if namespace == Some(sym::clippy)
|
||||
&& matches!(
|
||||
name.as_str(),
|
||||
"wildcard_imports"
|
||||
| "enum_glob_use"
|
||||
| "redundant_pub_crate"
|
||||
| "macro_use_imports"
|
||||
| "unsafe_removed_from_name"
|
||||
| "module_name_repetitions"
|
||||
| "single_component_path_imports"
|
||||
| "disallowed_types"
|
||||
| "unused_trait_names"
|
||||
name,
|
||||
sym::wildcard_imports
|
||||
| sym::enum_glob_use
|
||||
| sym::redundant_pub_crate
|
||||
| sym::macro_use_imports
|
||||
| sym::unsafe_removed_from_name
|
||||
| sym::module_name_repetitions
|
||||
| sym::single_component_path_imports
|
||||
| sym::disallowed_types
|
||||
| sym::unused_trait_names
|
||||
)
|
||||
{
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_config::types::{DisallowedPathWithoutReplacement, create_disallowed_map};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{match_def_path, paths};
|
||||
use clippy_utils::paths::{self, PathNS};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::{DefId, DefIdMap};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -182,6 +182,7 @@ impl AwaitHolding {
|
|||
let (def_ids, _) = create_disallowed_map(
|
||||
tcx,
|
||||
&conf.await_holding_invalid_types,
|
||||
PathNS::Type,
|
||||
crate::disallowed_types::def_kind_predicate,
|
||||
"type",
|
||||
false,
|
||||
|
|
@ -275,12 +276,10 @@ fn emit_invalid_type(
|
|||
}
|
||||
|
||||
fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
|
||||
cx.tcx.is_diagnostic_item(sym::MutexGuard, def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::RwLockReadGuard, def_id)
|
||||
|| cx.tcx.is_diagnostic_item(sym::RwLockWriteGuard, def_id)
|
||||
|| match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD)
|
||||
|| match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
|
||||
|| match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
|
||||
match cx.tcx.get_diagnostic_name(def_id) {
|
||||
Some(name) => matches!(name, sym::MutexGuard | sym::RwLockReadGuard | sym::RwLockWriteGuard),
|
||||
None => paths::PARKING_LOT_GUARDS.iter().any(|guard| guard.matches(cx, def_id)),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::sym;
|
||||
use clippy_utils::ty::{implements_trait, is_copy};
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -73,10 +74,9 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
|
|||
let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
|
||||
return;
|
||||
};
|
||||
let macro_name = cx.tcx.item_name(macro_call.def_id);
|
||||
let eq_macro = match macro_name.as_str() {
|
||||
"assert_eq" | "debug_assert_eq" => true,
|
||||
"assert_ne" | "debug_assert_ne" => false,
|
||||
let eq_macro = match cx.tcx.get_diagnostic_name(macro_call.def_id) {
|
||||
Some(sym::assert_eq_macro | sym::debug_assert_eq_macro) => true,
|
||||
Some(sym::assert_ne_macro | sym::debug_assert_ne_macro) => false,
|
||||
_ => return,
|
||||
};
|
||||
let Some((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else {
|
||||
|
|
@ -115,6 +115,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
|
|||
return;
|
||||
}
|
||||
|
||||
let macro_name = cx.tcx.item_name(macro_call.def_id);
|
||||
let macro_name = macro_name.as_str();
|
||||
let non_eq_mac = ¯o_name[..macro_name.len() - 3];
|
||||
span_lint_and_then(
|
||||
|
|
|
|||
82
clippy_lints/src/casts/confusing_method_to_numeric_cast.rs
Normal file
82
clippy_lints/src/casts/confusing_method_to_numeric_cast.rs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, GenericArg, Ty};
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::{Symbol, sym};
|
||||
|
||||
use super::CONFUSING_METHOD_TO_NUMERIC_CAST;
|
||||
|
||||
fn get_primitive_ty_name(ty: Ty<'_>) -> Option<&'static str> {
|
||||
match ty.kind() {
|
||||
ty::Char => Some("char"),
|
||||
ty::Int(int) => Some(int.name_str()),
|
||||
ty::Uint(uint) => Some(uint.name_str()),
|
||||
ty::Float(float) => Some(float.name_str()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_const_name_and_ty_name(
|
||||
cx: &LateContext<'_>,
|
||||
method_name: Symbol,
|
||||
method_def_id: DefId,
|
||||
generics: &[GenericArg<'_>],
|
||||
) -> Option<(&'static str, &'static str)> {
|
||||
let method_name = method_name.as_str();
|
||||
let diagnostic_name = cx.tcx.get_diagnostic_name(method_def_id);
|
||||
|
||||
let ty_name = if diagnostic_name.is_some_and(|diag| diag == sym::cmp_ord_min || diag == sym::cmp_ord_max) {
|
||||
// We get the type on which the `min`/`max` method of the `Ord` trait is implemented.
|
||||
if let [ty] = generics
|
||||
&& let Some(ty) = ty.as_type()
|
||||
{
|
||||
get_primitive_ty_name(ty)?
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
} else if let Some(impl_id) = cx.tcx.impl_of_method(method_def_id)
|
||||
&& let Some(ty_name) = get_primitive_ty_name(cx.tcx.type_of(impl_id).instantiate_identity())
|
||||
&& ["min", "max", "minimum", "maximum", "min_value", "max_value"].contains(&method_name)
|
||||
{
|
||||
ty_name
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let const_name = if method_name.starts_with("max") { "MAX" } else { "MIN" };
|
||||
Some((const_name, ty_name))
|
||||
}
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
|
||||
// We allow casts from any function type to any function type.
|
||||
match cast_to.kind() {
|
||||
ty::FnDef(..) | ty::FnPtr(..) => return,
|
||||
_ => { /* continue to checks */ },
|
||||
}
|
||||
|
||||
if let ty::FnDef(def_id, generics) = cast_from.kind()
|
||||
&& let Some(method_name) = cx.tcx.opt_item_name(*def_id)
|
||||
&& let Some((const_name, ty_name)) = get_const_name_and_ty_name(cx, method_name, *def_id, generics.as_slice())
|
||||
{
|
||||
let mut applicability = Applicability::MaybeIncorrect;
|
||||
let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability);
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
CONFUSING_METHOD_TO_NUMERIC_CAST,
|
||||
expr.span,
|
||||
format!("casting function pointer `{from_snippet}` to `{cast_to}`"),
|
||||
|diag| {
|
||||
diag.span_suggestion_verbose(
|
||||
expr.span,
|
||||
"did you mean to use the associated constant?",
|
||||
format!("{ty_name}::{const_name} as {cast_to}"),
|
||||
applicability,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::ty::is_normalizable;
|
||||
use clippy_utils::{expr_or_init, match_def_path, path_def_id, paths, std_or_core};
|
||||
use clippy_utils::{expr_or_init, path_def_id, paths, std_or_core};
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind};
|
||||
|
|
@ -55,7 +54,7 @@ fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) ->
|
|||
fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool {
|
||||
if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind
|
||||
&& let Some(fun_id) = path_def_id(cx, fun)
|
||||
&& match_def_path(cx, fun_id, &paths::ALIGN_OF)
|
||||
&& paths::ALIGN_OF.matches(cx, fun_id)
|
||||
&& let Some(args) = path.segments.last().and_then(|seg| seg.args)
|
||||
&& let [GenericArg::Type(generic_ty)] = args.args
|
||||
{
|
||||
|
|
@ -71,12 +70,10 @@ fn is_literal_aligned(cx: &LateContext<'_>, lit: &Spanned<LitKind>, to: &Ty<'_>)
|
|||
return false;
|
||||
}
|
||||
let to_mid_ty = cx.typeck_results().node_type(to.hir_id);
|
||||
is_normalizable(cx, cx.param_env, to_mid_ty)
|
||||
&& cx
|
||||
.tcx
|
||||
.layout_of(cx.typing_env().as_query_input(to_mid_ty))
|
||||
.is_ok_and(|layout| {
|
||||
let align = u128::from(layout.align.abi.bytes());
|
||||
u128::from(val) <= align
|
||||
})
|
||||
cx.tcx
|
||||
.layout_of(cx.typing_env().as_query_input(to_mid_ty))
|
||||
.is_ok_and(|layout| {
|
||||
let align = u128::from(layout.align.abi.bytes());
|
||||
u128::from(val) <= align
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ mod cast_sign_loss;
|
|||
mod cast_slice_different_sizes;
|
||||
mod cast_slice_from_raw_parts;
|
||||
mod char_lit_as_u8;
|
||||
mod confusing_method_to_numeric_cast;
|
||||
mod fn_to_numeric_cast;
|
||||
mod fn_to_numeric_cast_any;
|
||||
mod fn_to_numeric_cast_with_truncation;
|
||||
|
|
@ -780,12 +781,38 @@ declare_clippy_lint! {
|
|||
/// let aligned = std::ptr::dangling::<u32>();
|
||||
/// let mut_ptr: *mut i64 = std::ptr::dangling_mut();
|
||||
/// ```
|
||||
#[clippy::version = "1.87.0"]
|
||||
#[clippy::version = "1.88.0"]
|
||||
pub MANUAL_DANGLING_PTR,
|
||||
style,
|
||||
"casting small constant literals to pointers to create dangling pointers"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for casts of a primitive method pointer like `max`/`min` to any integer type.
|
||||
///
|
||||
/// ### Why restrict this?
|
||||
/// Casting a function pointer to an integer can have surprising results and can occur
|
||||
/// accidentally if parentheses are omitted from a function call. If you aren't doing anything
|
||||
/// low-level with function pointers then you can opt out of casting functions to integers in
|
||||
/// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
|
||||
/// pointer casts in your code.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let _ = u16::max as usize;
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// let _ = u16::MAX as usize;
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
pub CONFUSING_METHOD_TO_NUMERIC_CAST,
|
||||
suspicious,
|
||||
"casting a primitive method pointer to any integer type"
|
||||
}
|
||||
|
||||
pub struct Casts {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
|
@ -823,6 +850,7 @@ impl_lint_pass!(Casts => [
|
|||
REF_AS_PTR,
|
||||
AS_POINTER_UNDERSCORE,
|
||||
MANUAL_DANGLING_PTR,
|
||||
CONFUSING_METHOD_TO_NUMERIC_CAST,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
|
|
@ -847,6 +875,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv);
|
||||
as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to);
|
||||
fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to);
|
||||
confusing_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to);
|
||||
fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to);
|
||||
fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to);
|
||||
zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
|
||||
|
|
|
|||
100
clippy_lints/src/cloned_ref_to_slice_refs.rs
Normal file
100
clippy_lints/src/cloned_ref_to_slice_refs.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::sugg::Sugg;
|
||||
use clippy_utils::visitors::is_const_evaluatable;
|
||||
use clippy_utils::{is_in_const_context, is_mutable, is_trait_method};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Checks for slice references with cloned references such as `&[f.clone()]`.
|
||||
///
|
||||
/// ### Why is this bad
|
||||
///
|
||||
/// A reference does not need to be owned in order to used as a slice.
|
||||
///
|
||||
/// ### Known problems
|
||||
///
|
||||
/// This lint does not know whether or not a clone implementation has side effects.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// let data = 10;
|
||||
/// let data_ref = &data;
|
||||
/// take_slice(&[data_ref.clone()]);
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```ignore
|
||||
/// use std::slice;
|
||||
/// let data = 10;
|
||||
/// let data_ref = &data;
|
||||
/// take_slice(slice::from_ref(data_ref));
|
||||
/// ```
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub CLONED_REF_TO_SLICE_REFS,
|
||||
perf,
|
||||
"cloning a reference for slice references"
|
||||
}
|
||||
|
||||
pub struct ClonedRefToSliceRefs<'a> {
|
||||
msrv: &'a Msrv,
|
||||
}
|
||||
impl<'a> ClonedRefToSliceRefs<'a> {
|
||||
pub fn new(conf: &'a Conf) -> Self {
|
||||
Self { msrv: &conf.msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl_lint_pass!(ClonedRefToSliceRefs<'_> => [CLONED_REF_TO_SLICE_REFS]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
|
||||
if self.msrv.meets(cx, {
|
||||
if is_in_const_context(cx) {
|
||||
msrvs::CONST_SLICE_FROM_REF
|
||||
} else {
|
||||
msrvs::SLICE_FROM_REF
|
||||
}
|
||||
})
|
||||
// `&[foo.clone()]` expressions
|
||||
&& let ExprKind::AddrOf(_, mutability, arr) = &expr.kind
|
||||
// mutable references would have a different meaning
|
||||
&& mutability.is_not()
|
||||
|
||||
// check for single item arrays
|
||||
&& let ExprKind::Array([item]) = &arr.kind
|
||||
|
||||
// check for clones
|
||||
&& let ExprKind::MethodCall(_, val, _, _) = item.kind
|
||||
&& is_trait_method(cx, item, sym::Clone)
|
||||
|
||||
// check for immutability or purity
|
||||
&& (!is_mutable(cx, val) || is_const_evaluatable(cx, val))
|
||||
|
||||
// get appropriate crate for `slice::from_ref`
|
||||
&& let Some(builtin_crate) = clippy_utils::std_or_core(cx)
|
||||
{
|
||||
let mut sugg = Sugg::hir(cx, val, "_");
|
||||
if !cx.typeck_results().expr_ty(val).is_ref() {
|
||||
sugg = sugg.addr();
|
||||
}
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CLONED_REF_TO_SLICE_REFS,
|
||||
expr.span,
|
||||
format!("this call to `clone` can be replaced with `{builtin_crate}::slice::from_ref`"),
|
||||
"try",
|
||||
format!("{builtin_crate}::slice::from_ref({sugg})"),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block, snippet_block_with_applicability};
|
||||
use rustc_ast::BinOpKind;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Block, Expr, ExprKind, StmtKind};
|
||||
use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
||||
|
|
@ -78,14 +78,14 @@ declare_clippy_lint! {
|
|||
}
|
||||
|
||||
pub struct CollapsibleIf {
|
||||
let_chains_enabled: bool,
|
||||
msrv: Msrv,
|
||||
lint_commented_code: bool,
|
||||
}
|
||||
|
||||
impl CollapsibleIf {
|
||||
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
let_chains_enabled: tcx.features().let_chains(),
|
||||
msrv: conf.msrv,
|
||||
lint_commented_code: conf.lint_commented_code,
|
||||
}
|
||||
}
|
||||
|
|
@ -127,7 +127,7 @@ impl CollapsibleIf {
|
|||
if let Some(inner) = expr_block(then)
|
||||
&& cx.tcx.hir_attrs(inner.hir_id).is_empty()
|
||||
&& let ExprKind::If(check_inner, _, None) = &inner.kind
|
||||
&& self.eligible_condition(check_inner)
|
||||
&& self.eligible_condition(cx, check_inner)
|
||||
&& let ctxt = expr.span.ctxt()
|
||||
&& inner.span.ctxt() == ctxt
|
||||
&& (self.lint_commented_code || !block_starts_with_comment(cx, then))
|
||||
|
|
@ -163,8 +163,9 @@ impl CollapsibleIf {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn eligible_condition(&self, cond: &Expr<'_>) -> bool {
|
||||
self.let_chains_enabled || !matches!(cond.kind, ExprKind::Let(..))
|
||||
fn eligible_condition(&self, cx: &LateContext<'_>, cond: &Expr<'_>) -> bool {
|
||||
!matches!(cond.kind, ExprKind::Let(..))
|
||||
|| (cx.tcx.sess.edition().at_least_rust_2024() && self.msrv.meets(cx, msrvs::LET_CHAINS))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -180,7 +181,7 @@ impl LateLintPass<'_> for CollapsibleIf {
|
|||
{
|
||||
Self::check_collapsible_else_if(cx, then.span, else_);
|
||||
} else if else_.is_none()
|
||||
&& self.eligible_condition(cond)
|
||||
&& self.eligible_condition(cx, cond)
|
||||
&& let ExprKind::Block(then, None) = then.kind
|
||||
{
|
||||
self.check_collapsible_if_if(cx, expr, cond, then);
|
||||
|
|
@ -202,13 +203,12 @@ fn block_starts_with_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool {
|
|||
fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
match block.stmts {
|
||||
[] => block.expr,
|
||||
[stmt] => {
|
||||
if let StmtKind::Semi(expr) = stmt.kind {
|
||||
Some(expr)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
[
|
||||
Stmt {
|
||||
kind: StmtKind::Semi(expr),
|
||||
..
|
||||
},
|
||||
] if block.expr.is_none() => Some(expr),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::casts::CAST_SLICE_DIFFERENT_SIZES_INFO,
|
||||
crate::casts::CAST_SLICE_FROM_RAW_PARTS_INFO,
|
||||
crate::casts::CHAR_LIT_AS_U8_INFO,
|
||||
crate::casts::CONFUSING_METHOD_TO_NUMERIC_CAST_INFO,
|
||||
crate::casts::FN_TO_NUMERIC_CAST_INFO,
|
||||
crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO,
|
||||
crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO,
|
||||
|
|
@ -75,6 +76,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::casts::ZERO_PTR_INFO,
|
||||
crate::cfg_not_test::CFG_NOT_TEST_INFO,
|
||||
crate::checked_conversions::CHECKED_CONVERSIONS_INFO,
|
||||
crate::cloned_ref_to_slice_refs::CLONED_REF_TO_SLICE_REFS_INFO,
|
||||
crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
|
||||
crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO,
|
||||
crate::collapsible_if::COLLAPSIBLE_IF_INFO,
|
||||
|
|
@ -705,13 +707,9 @@ pub static LINTS: &[&crate::LintInfo] = &[
|
|||
crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS_INFO,
|
||||
crate::transmute::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS_INFO,
|
||||
crate::transmute::TRANSMUTE_BYTES_TO_STR_INFO,
|
||||
crate::transmute::TRANSMUTE_FLOAT_TO_INT_INFO,
|
||||
crate::transmute::TRANSMUTE_INT_TO_BOOL_INFO,
|
||||
crate::transmute::TRANSMUTE_INT_TO_CHAR_INFO,
|
||||
crate::transmute::TRANSMUTE_INT_TO_FLOAT_INFO,
|
||||
crate::transmute::TRANSMUTE_INT_TO_NON_ZERO_INFO,
|
||||
crate::transmute::TRANSMUTE_NULL_TO_FN_INFO,
|
||||
crate::transmute::TRANSMUTE_NUM_TO_BYTES_INFO,
|
||||
crate::transmute::TRANSMUTE_PTR_TO_PTR_INFO,
|
||||
crate::transmute::TRANSMUTE_PTR_TO_REF_INFO,
|
||||
crate::transmute::TRANSMUTE_UNDEFINED_REPR_INFO,
|
||||
|
|
|
|||
|
|
@ -2,18 +2,18 @@
|
|||
// Prefer to use those when possible.
|
||||
|
||||
macro_rules! declare_with_version {
|
||||
($name:ident($name_version:ident): &[$ty:ty] = &[$(
|
||||
($name:ident($name_version:ident) = [$(
|
||||
#[clippy::version = $version:literal]
|
||||
$e:expr,
|
||||
)*]) => {
|
||||
pub static $name: &[$ty] = &[$($e),*];
|
||||
pub static $name: &[(&str, &str)] = &[$($e),*];
|
||||
#[allow(unused)]
|
||||
pub static $name_version: &[&str] = &[$($version),*];
|
||||
};
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[
|
||||
declare_with_version! { DEPRECATED(DEPRECATED_VERSION) = [
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"),
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
|
|
@ -44,11 +44,10 @@ declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[
|
|||
("clippy::option_map_or_err_ok", "`clippy::manual_ok_or` covers this case"),
|
||||
#[clippy::version = "1.86.0"]
|
||||
("clippy::match_on_vec_items", "`clippy::indexing_slicing` covers indexing and slicing on `Vec<_>`"),
|
||||
// end deprecated lints. used by `cargo dev deprecate_lint`
|
||||
]}
|
||||
|
||||
#[rustfmt::skip]
|
||||
declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[
|
||||
declare_with_version! { RENAMED(RENAMED_VERSION) = [
|
||||
#[clippy::version = ""]
|
||||
("clippy::almost_complete_letter_range", "clippy::almost_complete_range"),
|
||||
#[clippy::version = ""]
|
||||
|
|
@ -187,5 +186,12 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[
|
|||
("clippy::vtable_address_comparisons", "ambiguous_wide_pointer_comparisons"),
|
||||
#[clippy::version = ""]
|
||||
("clippy::reverse_range_loop", "clippy::reversed_empty_ranges"),
|
||||
// end renamed lints. used by `cargo dev rename_lint`
|
||||
#[clippy::version = "1.88.0"]
|
||||
("clippy::transmute_int_to_float", "unnecessary_transmutes"),
|
||||
#[clippy::version = "1.88.0"]
|
||||
("clippy::transmute_int_to_char", "unnecessary_transmutes"),
|
||||
#[clippy::version = "1.88.0"]
|
||||
("clippy::transmute_float_to_int", "unnecessary_transmutes"),
|
||||
#[clippy::version = "1.88.0"]
|
||||
("clippy::transmute_num_to_bytes", "unnecessary_transmutes"),
|
||||
]}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use std::ops::ControlFlow;
|
|||
|
||||
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then, span_lint_hir_and_then};
|
||||
use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
|
||||
use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths};
|
||||
use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, paths};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item};
|
||||
|
|
@ -377,7 +377,7 @@ fn check_unsafe_derive_deserialize<'tcx>(
|
|||
}
|
||||
|
||||
if let Some(trait_def_id) = trait_ref.trait_def_id()
|
||||
&& match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE)
|
||||
&& paths::SERDE_DESERIALIZE.matches(cx, trait_def_id)
|
||||
&& let ty::Adt(def, _) = ty.kind()
|
||||
&& let Some(local_def_id) = def.did().as_local()
|
||||
&& let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
use clippy_config::Conf;
|
||||
use clippy_config::types::{DisallowedPath, create_disallowed_map};
|
||||
use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
|
||||
use clippy_utils::macros::macro_backtrace;
|
||||
use clippy_utils::paths::PathNS;
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
|
|
@ -76,6 +76,7 @@ impl DisallowedMacros {
|
|||
let (disallowed, _) = create_disallowed_map(
|
||||
tcx,
|
||||
&conf.disallowed_macros,
|
||||
PathNS::Macro,
|
||||
|def_kind| matches!(def_kind, DefKind::Macro(_)),
|
||||
"macro",
|
||||
false,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_config::types::{DisallowedPath, create_disallowed_map};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::paths::PathNS;
|
||||
use rustc_hir::def::{CtorKind, DefKind, Res};
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
|
|
@ -66,6 +67,7 @@ impl DisallowedMethods {
|
|||
let (disallowed, _) = create_disallowed_map(
|
||||
tcx,
|
||||
&conf.disallowed_methods,
|
||||
PathNS::Value,
|
||||
|def_kind| {
|
||||
matches!(
|
||||
def_kind,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_config::types::{DisallowedPath, create_disallowed_map};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::paths::PathNS;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
|
|
@ -60,7 +61,14 @@ pub struct DisallowedTypes {
|
|||
|
||||
impl DisallowedTypes {
|
||||
pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
|
||||
let (def_ids, prim_tys) = create_disallowed_map(tcx, &conf.disallowed_types, def_kind_predicate, "type", true);
|
||||
let (def_ids, prim_tys) = create_disallowed_map(
|
||||
tcx,
|
||||
&conf.disallowed_types,
|
||||
PathNS::Type,
|
||||
def_kind_predicate,
|
||||
"type",
|
||||
true,
|
||||
);
|
||||
Self { def_ids, prim_tys }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -93,7 +93,7 @@ declare_clippy_lint! {
|
|||
/// ```no_run
|
||||
/// //! <code>[first](x)second</code>
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub DOC_LINK_CODE,
|
||||
nursery,
|
||||
"link with code back-to-back with other code"
|
||||
|
|
|
|||
|
|
@ -759,12 +759,12 @@ impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
|
|||
let recv_ty = cx.typeck_results().expr_ty(receiver);
|
||||
|
||||
if recv_ty.is_floating_point() && !is_no_std_crate(cx) && is_inherent_method_call(cx, expr) {
|
||||
match path.ident.name.as_str() {
|
||||
"ln" => check_ln1p(cx, expr, receiver),
|
||||
"log" => check_log_base(cx, expr, receiver, args),
|
||||
"powf" => check_powf(cx, expr, receiver, args),
|
||||
"powi" => check_powi(cx, expr, receiver, args),
|
||||
"sqrt" => check_hypot(cx, expr, receiver),
|
||||
match path.ident.name {
|
||||
sym::ln => check_ln1p(cx, expr, receiver),
|
||||
sym::log => check_log_base(cx, expr, receiver, args),
|
||||
sym::powf => check_powf(cx, expr, receiver, args),
|
||||
sym::powi => check_powi(cx, expr, receiver, args),
|
||||
sym::sqrt => check_hypot(cx, expr, receiver),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ mod too_many_arguments;
|
|||
mod too_many_lines;
|
||||
|
||||
use clippy_config::Conf;
|
||||
use clippy_utils::def_path_def_ids;
|
||||
use clippy_utils::msrvs::Msrv;
|
||||
use clippy_utils::paths::{PathNS, lookup_path_str};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -469,7 +469,7 @@ impl Functions {
|
|||
trait_ids: conf
|
||||
.allow_renamed_params_for
|
||||
.iter()
|
||||
.flat_map(|p| def_path_def_ids(tcx, &p.split("::").collect::<Vec<_>>()))
|
||||
.flat_map(|p| lookup_path_str(tcx, PathNS::Type, p))
|
||||
.collect(),
|
||||
msrv: conf.msrv,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ declare_clippy_lint! {
|
|||
///
|
||||
/// let result = a.saturating_sub(b);
|
||||
/// ```
|
||||
#[clippy::version = "1.44.0"]
|
||||
#[clippy::version = "1.83.0"]
|
||||
pub INVERTED_SATURATING_SUB,
|
||||
correctness,
|
||||
"Check if a variable is smaller than another one and still subtract from it even if smaller"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use clippy_utils::macros::span_is_local;
|
|||
use clippy_utils::source::is_present_in_source;
|
||||
use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_start, to_camel_case, to_snake_case};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData};
|
||||
use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, QPath, TyKind, Variant, VariantData};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::symbol::Symbol;
|
||||
|
|
@ -162,6 +162,7 @@ pub struct ItemNameRepetitions {
|
|||
enum_threshold: u64,
|
||||
struct_threshold: u64,
|
||||
avoid_breaking_exported_api: bool,
|
||||
allow_exact_repetitions: bool,
|
||||
allow_private_module_inception: bool,
|
||||
allowed_prefixes: FxHashSet<String>,
|
||||
}
|
||||
|
|
@ -173,6 +174,7 @@ impl ItemNameRepetitions {
|
|||
enum_threshold: conf.enum_variant_name_threshold,
|
||||
struct_threshold: conf.struct_field_name_threshold,
|
||||
avoid_breaking_exported_api: conf.avoid_breaking_exported_api,
|
||||
allow_exact_repetitions: conf.allow_exact_repetitions,
|
||||
allow_private_module_inception: conf.allow_private_module_inception,
|
||||
allowed_prefixes: conf.allowed_prefixes.iter().map(|s| to_camel_case(s)).collect(),
|
||||
}
|
||||
|
|
@ -405,6 +407,7 @@ fn check_enum_start(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>
|
|||
if count_match_start(item_name, name).char_count == item_name_chars
|
||||
&& name.chars().nth(item_name_chars).is_some_and(|c| !c.is_lowercase())
|
||||
&& name.chars().nth(item_name_chars + 1).is_some_and(|c| !c.is_numeric())
|
||||
&& !check_enum_tuple_path_match(name, variant.data)
|
||||
{
|
||||
span_lint_hir(
|
||||
cx,
|
||||
|
|
@ -420,7 +423,9 @@ fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>)
|
|||
let name = variant.ident.name.as_str();
|
||||
let item_name_chars = item_name.chars().count();
|
||||
|
||||
if count_match_end(item_name, name).char_count == item_name_chars {
|
||||
if count_match_end(item_name, name).char_count == item_name_chars
|
||||
&& !check_enum_tuple_path_match(name, variant.data)
|
||||
{
|
||||
span_lint_hir(
|
||||
cx,
|
||||
ENUM_VARIANT_NAMES,
|
||||
|
|
@ -431,6 +436,27 @@ fn check_enum_end(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>)
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks if an enum tuple variant contains a single field
|
||||
/// whose qualified path contains the variant's name.
|
||||
fn check_enum_tuple_path_match(variant_name: &str, variant_data: VariantData<'_>) -> bool {
|
||||
// Only check single-field tuple variants
|
||||
let VariantData::Tuple(fields, ..) = variant_data else {
|
||||
return false;
|
||||
};
|
||||
if fields.len() != 1 {
|
||||
return false;
|
||||
}
|
||||
// Check if field type is a path and contains the variant name
|
||||
match fields[0].ty.kind {
|
||||
TyKind::Path(QPath::Resolved(_, path)) => path
|
||||
.segments
|
||||
.iter()
|
||||
.any(|segment| segment.ident.name.as_str() == variant_name),
|
||||
TyKind::Path(QPath::TypeRelative(_, segment)) => segment.ident.name.as_str() == variant_name,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
impl LateLintPass<'_> for ItemNameRepetitions {
|
||||
fn check_item_post(&mut self, _cx: &LateContext<'_>, item: &Item<'_>) {
|
||||
let Some(_ident) = item.kind.ident() else { return };
|
||||
|
|
@ -462,11 +488,21 @@ impl LateLintPass<'_> for ItemNameRepetitions {
|
|||
}
|
||||
|
||||
// The `module_name_repetitions` lint should only trigger if the item has the module in its
|
||||
// name. Having the same name is accepted.
|
||||
if cx.tcx.visibility(item.owner_id).is_public()
|
||||
&& cx.tcx.visibility(mod_owner_id.def_id).is_public()
|
||||
&& item_camel.len() > mod_camel.len()
|
||||
{
|
||||
// name. Having the same name is only accepted if `allow_exact_repetition` is set to `true`.
|
||||
|
||||
let both_are_public =
|
||||
cx.tcx.visibility(item.owner_id).is_public() && cx.tcx.visibility(mod_owner_id.def_id).is_public();
|
||||
|
||||
if both_are_public && !self.allow_exact_repetitions && item_camel == *mod_camel {
|
||||
span_lint(
|
||||
cx,
|
||||
MODULE_NAME_REPETITIONS,
|
||||
ident.span,
|
||||
"item name is the same as its containing module's name",
|
||||
);
|
||||
}
|
||||
|
||||
if both_are_public && item_camel.len() > mod_camel.len() {
|
||||
let matching = count_match_start(mod_camel, &item_camel);
|
||||
let rmatching = count_match_end(mod_camel, &item_camel);
|
||||
let nchars = mod_camel.chars().count();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type};
|
||||
use clippy_utils::ty::{implements_trait, is_must_use_ty};
|
||||
use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths};
|
||||
use rustc_hir::{LetStmt, LocalSource, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -129,12 +129,6 @@ declare_clippy_lint! {
|
|||
|
||||
declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE, LET_UNDERSCORE_UNTYPED]);
|
||||
|
||||
const SYNC_GUARD_PATHS: [&[&str]; 3] = [
|
||||
&paths::PARKING_LOT_MUTEX_GUARD,
|
||||
&paths::PARKING_LOT_RWLOCK_READ_GUARD,
|
||||
&paths::PARKING_LOT_RWLOCK_WRITE_GUARD,
|
||||
];
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
||||
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) {
|
||||
if matches!(local.source, LocalSource::Normal)
|
||||
|
|
@ -144,7 +138,9 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
|||
{
|
||||
let init_ty = cx.typeck_results().expr_ty(init);
|
||||
let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
|
||||
GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)),
|
||||
GenericArgKind::Type(inner_ty) => inner_ty
|
||||
.ty_adt_def()
|
||||
.is_some_and(|adt| paths::PARKING_LOT_GUARDS.iter().any(|path| path.matches(cx, adt.did()))),
|
||||
GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
|
||||
});
|
||||
if contains_sync_guard {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::is_from_proc_macro;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{LetStmt, TyKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
|
@ -32,13 +33,19 @@ impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped {
|
|||
&& !local.span.in_external_macro(cx.tcx.sess.source_map())
|
||||
&& !is_from_proc_macro(cx, ty)
|
||||
{
|
||||
span_lint_and_help(
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
LET_WITH_TYPE_UNDERSCORE,
|
||||
local.span,
|
||||
"variable declared with type underscore",
|
||||
Some(ty.span.with_lo(local.pat.span.hi())),
|
||||
"remove the explicit type `_` declaration",
|
||||
|diag| {
|
||||
diag.span_suggestion_verbose(
|
||||
ty.span.with_lo(local.pat.span.hi()),
|
||||
"remove the explicit type `_` declaration",
|
||||
"",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
#![feature(array_windows)]
|
||||
#![feature(binary_heap_into_iter_sorted)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(macro_metavar_expr_concat)]
|
||||
#![feature(f128)]
|
||||
|
|
@ -7,7 +6,6 @@
|
|||
#![feature(if_let_guard)]
|
||||
#![feature(iter_intersperse)]
|
||||
#![feature(iter_partition_in_place)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(never_type)]
|
||||
#![feature(round_char_boundary)]
|
||||
#![feature(rustc_private)]
|
||||
|
|
@ -96,6 +94,7 @@ mod cargo;
|
|||
mod casts;
|
||||
mod cfg_not_test;
|
||||
mod checked_conversions;
|
||||
mod cloned_ref_to_slice_refs;
|
||||
mod cognitive_complexity;
|
||||
mod collapsible_if;
|
||||
mod collection_is_never_read;
|
||||
|
|
@ -731,7 +730,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
store.register_early_pass(|| Box::new(unused_unit::UnusedUnit));
|
||||
store.register_late_pass(|_| Box::new(unused_unit::UnusedUnit));
|
||||
store.register_late_pass(|_| Box::new(returns::Return));
|
||||
store.register_late_pass(move |tcx| Box::new(collapsible_if::CollapsibleIf::new(tcx, conf)));
|
||||
store.register_late_pass(move |_| Box::new(collapsible_if::CollapsibleIf::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(items_after_statements::ItemsAfterStatements));
|
||||
store.register_early_pass(|| Box::new(precedence::Precedence));
|
||||
store.register_late_pass(|_| Box::new(needless_parens_on_range_literals::NeedlessParensOnRangeLiterals));
|
||||
|
|
@ -748,7 +747,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
store.register_late_pass(move |_| Box::new(unused_self::UnusedSelf::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
|
||||
store.register_late_pass(|_| Box::new(exit::Exit));
|
||||
store.register_late_pass(|_| Box::new(to_digit_is_some::ToDigitIsSome));
|
||||
store.register_late_pass(move |_| Box::new(to_digit_is_some::ToDigitIsSome::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(conf)));
|
||||
store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic));
|
||||
|
|
@ -944,5 +943,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
|
|||
store.register_late_pass(|_| Box::new(manual_option_as_slice::ManualOptionAsSlice::new(conf)));
|
||||
store.register_late_pass(|_| Box::new(single_option_map::SingleOptionMap));
|
||||
store.register_late_pass(move |_| Box::new(redundant_test_prefix::RedundantTestPrefix));
|
||||
store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf)));
|
||||
// add lints here, do not remove this comment, it's used in `new_lint`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ declare_clippy_lint! {
|
|||
/// x.chars()
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.84.0"]
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub ELIDABLE_LIFETIME_NAMES,
|
||||
pedantic,
|
||||
"lifetime name that can be replaced with the anonymous lifetime"
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
|||
use clippy_utils::eager_or_lazy::switch_to_eager_eval;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::{HasSession, snippet_with_applicability};
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::ty::{implements_trait, is_slice_like};
|
||||
use clippy_utils::visitors::is_local_used;
|
||||
use clippy_utils::{higher, peel_blocks_with_stmt, span_contains_comment};
|
||||
use rustc_ast::ast::LitKind;
|
||||
|
|
@ -58,6 +58,8 @@ pub(super) fn check<'tcx>(
|
|||
&& let Res::Local(idx_hir) = idx_path.res
|
||||
&& !is_local_used(cx, assignval, idx_hir)
|
||||
&& msrv.meets(cx, msrvs::SLICE_FILL)
|
||||
&& let slice_ty = cx.typeck_results().expr_ty(slice).peel_refs()
|
||||
&& is_slice_like(cx, slice_ty)
|
||||
{
|
||||
sugg(cx, body, expr, slice.span, assignval.span);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -778,7 +778,7 @@ declare_clippy_lint! {
|
|||
/// let _ = s[idx..];
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.83.0"]
|
||||
#[clippy::version = "1.88.0"]
|
||||
pub CHAR_INDICES_AS_BYTE_INDICES,
|
||||
correctness,
|
||||
"using the character position yielded by `.chars().enumerate()` in a context where a byte index is expected"
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ declare_clippy_lint! {
|
|||
/// a.abs_diff(b)
|
||||
/// # ;
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
#[clippy::version = "1.88.0"]
|
||||
pub MANUAL_ABS_DIFF,
|
||||
complexity,
|
||||
"using an if-else pattern instead of `abs_diff`"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use crate::manual_ignore_case_cmp::MatchType::{Literal, ToAscii};
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::sym;
|
||||
use clippy_utils::ty::{get_type_diagnostic_name, is_type_diagnostic_item, is_type_lang_item};
|
||||
use rustc_ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -10,7 +11,7 @@ use rustc_lint::{LateContext, LateLintPass};
|
|||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::{Ty, UintTy};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::{Span, sym};
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -47,9 +48,9 @@ enum MatchType<'a, 'b> {
|
|||
|
||||
fn get_ascii_type<'a, 'b>(cx: &LateContext<'a>, kind: rustc_hir::ExprKind<'b>) -> Option<(Span, MatchType<'a, 'b>)> {
|
||||
if let MethodCall(path, expr, _, _) = kind {
|
||||
let is_lower = match path.ident.name.as_str() {
|
||||
"to_ascii_lowercase" => true,
|
||||
"to_ascii_uppercase" => false,
|
||||
let is_lower = match path.ident.name {
|
||||
sym::to_ascii_lowercase => true,
|
||||
sym::to_ascii_uppercase => false,
|
||||
_ => return None,
|
||||
};
|
||||
let ty_raw = cx.typeck_results().expr_ty(expr);
|
||||
|
|
|
|||
|
|
@ -4,11 +4,14 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::higher::IfLetOrMatch;
|
||||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::{is_lint_allowed, is_never_expr, msrvs, pat_and_expr_can_be_question_mark, peel_blocks};
|
||||
use clippy_utils::{
|
||||
MaybePath, is_lint_allowed, is_never_expr, is_wild, msrvs, pat_and_expr_can_be_question_mark, path_res, peel_blocks,
|
||||
};
|
||||
use rustc_ast::BindingMode;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind};
|
||||
use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind};
|
||||
use rustc_lint::{LateContext, LintContext};
|
||||
|
||||
use rustc_span::Span;
|
||||
|
|
@ -91,14 +94,15 @@ impl<'tcx> QuestionMark {
|
|||
let Some((idx, diverging_arm)) = diverging_arm_opt else {
|
||||
return;
|
||||
};
|
||||
|
||||
let pat_arm = &arms[1 - idx];
|
||||
// If the non-diverging arm is the first one, its pattern can be reused in a let/else statement.
|
||||
// However, if it arrives in second position, its pattern may cover some cases already covered
|
||||
// by the diverging one.
|
||||
// TODO: accept the non-diverging arm as a second position if patterns are disjointed.
|
||||
if idx == 0 {
|
||||
if idx == 0 && !is_arms_disjointed(cx, diverging_arm, pat_arm) {
|
||||
return;
|
||||
}
|
||||
let pat_arm = &arms[1 - idx];
|
||||
|
||||
let Some(ident_map) = expr_simple_identity_map(local.pat, pat_arm.pat, pat_arm.body) else {
|
||||
return;
|
||||
};
|
||||
|
|
@ -110,6 +114,63 @@ impl<'tcx> QuestionMark {
|
|||
}
|
||||
}
|
||||
|
||||
/// Checks if the patterns of the arms are disjointed. Currently, we only support patterns of simple
|
||||
/// enum variants without nested patterns or bindings.
|
||||
///
|
||||
/// TODO: Support more complex patterns.
|
||||
fn is_arms_disjointed(cx: &LateContext<'_>, arm1: &Arm<'_>, arm2: &Arm<'_>) -> bool {
|
||||
if arm1.guard.is_some() || arm2.guard.is_some() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !is_enum_variant(cx, arm1.pat) || !is_enum_variant(cx, arm2.pat) {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns `true` if the given pattern is a variant of an enum.
|
||||
pub fn is_enum_variant(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
|
||||
struct Pat<'hir>(&'hir rustc_hir::Pat<'hir>);
|
||||
|
||||
impl<'hir> MaybePath<'hir> for Pat<'hir> {
|
||||
fn qpath_opt(&self) -> Option<&QPath<'hir>> {
|
||||
match self.0.kind {
|
||||
PatKind::Struct(ref qpath, fields, _)
|
||||
if fields
|
||||
.iter()
|
||||
.all(|field| is_wild(field.pat) || matches!(field.pat.kind, PatKind::Binding(..))) =>
|
||||
{
|
||||
Some(qpath)
|
||||
},
|
||||
PatKind::TupleStruct(ref qpath, pats, _)
|
||||
if pats
|
||||
.iter()
|
||||
.all(|pat| is_wild(pat) || matches!(pat.kind, PatKind::Binding(..))) =>
|
||||
{
|
||||
Some(qpath)
|
||||
},
|
||||
PatKind::Expr(&PatExpr {
|
||||
kind: PatExprKind::Path(ref qpath),
|
||||
..
|
||||
}) => Some(qpath),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn hir_id(&self) -> HirId {
|
||||
self.0.hir_id
|
||||
}
|
||||
}
|
||||
|
||||
let res = path_res(cx, &Pat(pat));
|
||||
matches!(
|
||||
res,
|
||||
Res::Def(DefKind::Variant, ..) | Res::Def(DefKind::Ctor(CtorOf::Variant, _), _)
|
||||
)
|
||||
}
|
||||
|
||||
fn emit_manual_let_else(
|
||||
cx: &LateContext<'_>,
|
||||
span: Span,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
|
||||
use clippy_utils::msrvs::Msrv;
|
||||
use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs, sym};
|
||||
use clippy_utils::{is_none_arm, msrvs, paths, peel_hir_expr_refs, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal};
|
||||
|
|
@ -80,26 +80,26 @@ impl LateLintPass<'_> for ManualOptionAsSlice {
|
|||
check_map(cx, callee, span, self.msrv);
|
||||
}
|
||||
},
|
||||
ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name.as_str() {
|
||||
"unwrap_or" => {
|
||||
ExprKind::MethodCall(seg, callee, [or], _) => match seg.ident.name {
|
||||
sym::unwrap_or => {
|
||||
if is_empty_slice(cx, or) {
|
||||
check_map(cx, callee, span, self.msrv);
|
||||
}
|
||||
},
|
||||
"unwrap_or_else" => {
|
||||
sym::unwrap_or_else => {
|
||||
if returns_empty_slice(cx, or) {
|
||||
check_map(cx, callee, span, self.msrv);
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
},
|
||||
ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name.as_str() {
|
||||
"map_or" => {
|
||||
ExprKind::MethodCall(seg, callee, [or_else, map], _) => match seg.ident.name {
|
||||
sym::map_or => {
|
||||
if is_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) {
|
||||
check_as_ref(cx, callee, span, self.msrv);
|
||||
}
|
||||
},
|
||||
"map_or_else" => {
|
||||
sym::map_or_else => {
|
||||
if returns_empty_slice(cx, or_else) && is_slice_from_ref(cx, map) {
|
||||
check_as_ref(cx, callee, span, self.msrv);
|
||||
}
|
||||
|
|
@ -220,5 +220,5 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
|||
}
|
||||
|
||||
fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
clippy_utils::is_expr_path_def_path(cx, expr, &["core", "slice", "raw", "from_ref"])
|
||||
paths::SLICE_FROM_REF.matches_path(cx, expr)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use clippy_utils::consts::ConstEvalCtxt;
|
||||
use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline};
|
||||
use rustc_ast::{BindingMode, ByRef};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{Arm, Expr, ExprKind, HirId, LangItem, Pat, PatExpr, PatExprKind, PatKind, QPath};
|
||||
|
|
@ -16,7 +17,7 @@ use super::{MANUAL_UNWRAP_OR, MANUAL_UNWRAP_OR_DEFAULT};
|
|||
|
||||
fn get_some(cx: &LateContext<'_>, pat: &Pat<'_>) -> Option<HirId> {
|
||||
if let PatKind::TupleStruct(QPath::Resolved(_, path), &[pat], _) = pat.kind
|
||||
&& let PatKind::Binding(_, pat_id, _, _) = pat.kind
|
||||
&& let PatKind::Binding(BindingMode(ByRef::No, _), pat_id, _, _) = pat.kind
|
||||
&& let Some(def_id) = path.res.opt_def_id()
|
||||
// Since it comes from a pattern binding, we need to get the parent to actually match
|
||||
// against it.
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ declare_clippy_lint! {
|
|||
/// let mut an_option = Some(0);
|
||||
/// let taken = an_option.replace(1);
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub MEM_REPLACE_OPTION_WITH_SOME,
|
||||
style,
|
||||
"replacing an `Option` with `Some` instead of `replace()`"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline};
|
||||
use clippy_utils::sym;
|
||||
use clippy_utils::ty::is_type_lang_item;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -21,8 +22,8 @@ pub(super) fn check<'tcx>(
|
|||
) {
|
||||
if let ExprKind::MethodCall(path_segment, ..) = recv.kind
|
||||
&& matches!(
|
||||
path_segment.ident.name.as_str(),
|
||||
"to_lowercase" | "to_uppercase" | "to_ascii_lowercase" | "to_ascii_uppercase"
|
||||
path_segment.ident.name,
|
||||
sym::to_lowercase | sym::to_uppercase | sym::to_ascii_lowercase | sym::to_ascii_uppercase
|
||||
)
|
||||
{
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::{expr_or_init, paths};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -8,13 +9,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args
|
|||
if let [error_kind, error] = args
|
||||
&& !expr.span.from_expansion()
|
||||
&& !error_kind.span.from_expansion()
|
||||
&& clippy_utils::is_expr_path_def_path(cx, path, &clippy_utils::paths::IO_ERROR_NEW)
|
||||
&& clippy_utils::is_expr_path_def_path(
|
||||
cx,
|
||||
clippy_utils::expr_or_init(cx, error_kind),
|
||||
&clippy_utils::paths::IO_ERRORKIND_OTHER,
|
||||
)
|
||||
&& let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind
|
||||
&& paths::IO_ERROR_NEW.matches_path(cx, path)
|
||||
&& paths::IO_ERRORKIND_OTHER_CTOR.matches_path(cx, expr_or_init(cx, error_kind))
|
||||
&& msrv.meets(cx, msrvs::IO_ERROR_OTHER)
|
||||
{
|
||||
span_lint_and_then(
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::get_parent_expr;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet;
|
||||
use clippy_utils::{get_parent_expr, sym};
|
||||
use rustc_ast::{LitKind, StrStyle};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, Node, QPath, TyKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::edition::Edition::Edition2021;
|
||||
use rustc_span::{Span, Symbol, sym};
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
use super::MANUAL_C_STR_LITERALS;
|
||||
|
||||
|
|
@ -71,15 +71,15 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args
|
|||
&& cx.tcx.sess.edition() >= Edition2021
|
||||
&& msrv.meets(cx, msrvs::C_STR_LITERALS)
|
||||
{
|
||||
match fn_name.as_str() {
|
||||
name @ ("from_bytes_with_nul" | "from_bytes_with_nul_unchecked")
|
||||
match fn_name {
|
||||
sym::from_bytes_with_nul | sym::from_bytes_with_nul_unchecked
|
||||
if !arg.span.from_expansion()
|
||||
&& let ExprKind::Lit(lit) = arg.kind
|
||||
&& let LitKind::ByteStr(_, StrStyle::Cooked) | LitKind::Str(_, StrStyle::Cooked) = lit.node =>
|
||||
{
|
||||
check_from_bytes(cx, expr, arg, name);
|
||||
check_from_bytes(cx, expr, arg, fn_name);
|
||||
},
|
||||
"from_ptr" => check_from_ptr(cx, expr, arg),
|
||||
sym::from_ptr => check_from_ptr(cx, expr, arg),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
|
@ -106,13 +106,13 @@ fn check_from_ptr(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>) {
|
|||
}
|
||||
}
|
||||
/// Checks `CStr::from_bytes_with_nul(b"foo\0")`
|
||||
fn check_from_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, method: &str) {
|
||||
fn check_from_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, method: Symbol) {
|
||||
let (span, applicability) = if let Some(parent) = get_parent_expr(cx, expr)
|
||||
&& let ExprKind::MethodCall(method, ..) = parent.kind
|
||||
&& [sym::unwrap, sym::expect].contains(&method.ident.name)
|
||||
{
|
||||
(parent.span, Applicability::MachineApplicable)
|
||||
} else if method == "from_bytes_with_nul_unchecked" {
|
||||
} else if method == sym::from_bytes_with_nul_unchecked {
|
||||
// `*_unchecked` returns `&CStr` directly, nothing needs to be changed
|
||||
(expr.span, Applicability::MachineApplicable)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{match_def_path, path_def_id};
|
||||
use clippy_utils::{path_res, sym};
|
||||
use rustc_ast::ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
|
||||
|
|
@ -79,16 +80,15 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
|
|||
}
|
||||
|
||||
let ty = cx.typeck_results().expr_ty(expr);
|
||||
let ty_str = ty.to_string();
|
||||
|
||||
// `std::T::MAX` `std::T::MIN` constants
|
||||
if let Some(id) = path_def_id(cx, expr) {
|
||||
if match_def_path(cx, id, &["core", &ty_str, "MAX"]) {
|
||||
return Some(MinMax::Max);
|
||||
}
|
||||
|
||||
if match_def_path(cx, id, &["core", &ty_str, "MIN"]) {
|
||||
return Some(MinMax::Min);
|
||||
// `T::MAX` and `T::MIN` constants
|
||||
if let hir::ExprKind::Path(hir::QPath::TypeRelative(base, seg)) = expr.kind
|
||||
&& let Res::PrimTy(_) = path_res(cx, base)
|
||||
{
|
||||
match seg.ident.name {
|
||||
sym::MAX => return Some(MinMax::Max),
|
||||
sym::MIN => return Some(MinMax::Min),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4428,7 +4428,7 @@ declare_clippy_lint! {
|
|||
/// let file = BufReader::new(std::fs::File::open("./bytes.txt").unwrap());
|
||||
/// file.bytes();
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub UNBUFFERED_BYTES,
|
||||
perf,
|
||||
"calling .bytes() is very inefficient when data is not in memory"
|
||||
|
|
@ -4453,7 +4453,7 @@ declare_clippy_lint! {
|
|||
/// values.contains(&10)
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub MANUAL_CONTAINS,
|
||||
perf,
|
||||
"unnecessary `iter().any()` on slices that can be replaced with `contains()`"
|
||||
|
|
@ -4709,6 +4709,8 @@ impl_lint_pass!(Methods => [
|
|||
]);
|
||||
|
||||
/// Extracts a method call name, args, and `Span` of the method name.
|
||||
/// This ensures that neither the receiver nor any of the arguments
|
||||
/// come from expansion.
|
||||
pub fn method_call<'tcx>(
|
||||
recv: &'tcx Expr<'tcx>,
|
||||
) -> Option<(&'tcx str, &'tcx Expr<'tcx>, &'tcx [Expr<'tcx>], Span, Span)> {
|
||||
|
|
@ -4907,6 +4909,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
|
|||
impl Methods {
|
||||
#[allow(clippy::too_many_lines)]
|
||||
fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
// Handle method calls whose receiver and arguments may not come from expansion
|
||||
if let Some((name, recv, args, span, call_span)) = method_call(expr) {
|
||||
match (name, args) {
|
||||
("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => {
|
||||
|
|
@ -5049,29 +5052,12 @@ impl Methods {
|
|||
Some(("err", recv, [], err_span, _)) => {
|
||||
err_expect::check(cx, expr, recv, span, err_span, self.msrv);
|
||||
},
|
||||
_ => unwrap_expect_used::check(
|
||||
cx,
|
||||
expr,
|
||||
recv,
|
||||
false,
|
||||
self.allow_expect_in_consts,
|
||||
self.allow_expect_in_tests,
|
||||
unwrap_expect_used::Variant::Expect,
|
||||
),
|
||||
_ => {},
|
||||
}
|
||||
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||
},
|
||||
("expect_err", [_]) => {
|
||||
("expect_err", [_]) | ("unwrap_err" | "unwrap_unchecked" | "unwrap_err_unchecked", []) => {
|
||||
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||
unwrap_expect_used::check(
|
||||
cx,
|
||||
expr,
|
||||
recv,
|
||||
true,
|
||||
self.allow_expect_in_consts,
|
||||
self.allow_expect_in_tests,
|
||||
unwrap_expect_used::Variant::Expect,
|
||||
);
|
||||
},
|
||||
("extend", [arg]) => {
|
||||
string_extend_chars::check(cx, expr, recv, arg);
|
||||
|
|
@ -5437,27 +5423,6 @@ impl Methods {
|
|||
_ => {},
|
||||
}
|
||||
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||
unwrap_expect_used::check(
|
||||
cx,
|
||||
expr,
|
||||
recv,
|
||||
false,
|
||||
self.allow_unwrap_in_consts,
|
||||
self.allow_unwrap_in_tests,
|
||||
unwrap_expect_used::Variant::Unwrap,
|
||||
);
|
||||
},
|
||||
("unwrap_err", []) => {
|
||||
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||
unwrap_expect_used::check(
|
||||
cx,
|
||||
expr,
|
||||
recv,
|
||||
true,
|
||||
self.allow_unwrap_in_consts,
|
||||
self.allow_unwrap_in_tests,
|
||||
unwrap_expect_used::Variant::Unwrap,
|
||||
);
|
||||
},
|
||||
("unwrap_or", [u_arg]) => {
|
||||
match method_call(recv) {
|
||||
|
|
@ -5486,9 +5451,6 @@ impl Methods {
|
|||
}
|
||||
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||
},
|
||||
("unwrap_unchecked" | "unwrap_err_unchecked", []) => {
|
||||
unnecessary_literal_unwrap::check(cx, expr, recv, name, args);
|
||||
},
|
||||
("unwrap_or_else", [u_arg]) => {
|
||||
match method_call(recv) {
|
||||
Some(("map", recv, [map_arg], _, _))
|
||||
|
|
@ -5526,6 +5488,56 @@ impl Methods {
|
|||
_ => {},
|
||||
}
|
||||
}
|
||||
// Handle method calls whose receiver and arguments may come from expansion
|
||||
if let ExprKind::MethodCall(path, recv, args, _call_span) = expr.kind {
|
||||
match (path.ident.name.as_str(), args) {
|
||||
("expect", [_]) if !matches!(method_call(recv), Some(("ok" | "err", _, [], _, _))) => {
|
||||
unwrap_expect_used::check(
|
||||
cx,
|
||||
expr,
|
||||
recv,
|
||||
false,
|
||||
self.allow_expect_in_consts,
|
||||
self.allow_expect_in_tests,
|
||||
unwrap_expect_used::Variant::Expect,
|
||||
);
|
||||
},
|
||||
("expect_err", [_]) => {
|
||||
unwrap_expect_used::check(
|
||||
cx,
|
||||
expr,
|
||||
recv,
|
||||
true,
|
||||
self.allow_expect_in_consts,
|
||||
self.allow_expect_in_tests,
|
||||
unwrap_expect_used::Variant::Expect,
|
||||
);
|
||||
},
|
||||
("unwrap", []) => {
|
||||
unwrap_expect_used::check(
|
||||
cx,
|
||||
expr,
|
||||
recv,
|
||||
false,
|
||||
self.allow_unwrap_in_consts,
|
||||
self.allow_unwrap_in_tests,
|
||||
unwrap_expect_used::Variant::Unwrap,
|
||||
);
|
||||
},
|
||||
("unwrap_err", []) => {
|
||||
unwrap_expect_used::check(
|
||||
cx,
|
||||
expr,
|
||||
recv,
|
||||
true,
|
||||
self.allow_unwrap_in_consts,
|
||||
self.allow_unwrap_in_tests,
|
||||
unwrap_expect_used::Variant::Unwrap,
|
||||
);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,9 +7,8 @@ use rustc_span::Span;
|
|||
use super::NEEDLESS_CHARACTER_ITERATION;
|
||||
use super::utils::get_last_chain_binding_hir_id;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::paths::CHAR_IS_ASCII;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::{match_def_path, path_to_local_id, peel_blocks, sym};
|
||||
use clippy_utils::{is_path_diagnostic_item, path_to_local_id, peel_blocks, sym};
|
||||
|
||||
fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> {
|
||||
while let ExprKind::AddrOf(_, _, e) = expr.kind {
|
||||
|
|
@ -76,9 +75,7 @@ fn handle_expr(
|
|||
// If we have `!is_ascii`, then only `.any()` should warn. And if the condition is
|
||||
// `is_ascii`, then only `.all()` should warn.
|
||||
if revert != is_all
|
||||
&& let ExprKind::Path(path) = fn_path.kind
|
||||
&& let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id()
|
||||
&& match_def_path(cx, fn_def_id, &CHAR_IS_ASCII)
|
||||
&& is_path_diagnostic_item(cx, fn_path, sym::char_is_ascii)
|
||||
&& path_to_local_id(peels_expr_ref(arg), first_param)
|
||||
&& let Some(snippet) = before_chars.get_source_text(cx)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -357,20 +357,20 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
|
|||
if let Some(hir_id) = self.current_statement_hir_id {
|
||||
self.hir_id_uses_map.insert(hir_id, self.uses.len());
|
||||
}
|
||||
match method_name.ident.name.as_str() {
|
||||
"into_iter" => self.uses.push(Some(IterFunction {
|
||||
match method_name.ident.name {
|
||||
sym::into_iter => self.uses.push(Some(IterFunction {
|
||||
func: IterFunctionKind::IntoIter(expr.hir_id),
|
||||
span: expr.span,
|
||||
})),
|
||||
"len" => self.uses.push(Some(IterFunction {
|
||||
sym::len => self.uses.push(Some(IterFunction {
|
||||
func: IterFunctionKind::Len,
|
||||
span: expr.span,
|
||||
})),
|
||||
"is_empty" => self.uses.push(Some(IterFunction {
|
||||
sym::is_empty => self.uses.push(Some(IterFunction {
|
||||
func: IterFunctionKind::IsEmpty,
|
||||
span: expr.span,
|
||||
})),
|
||||
"contains" => self.uses.push(Some(IterFunction {
|
||||
sym::contains => self.uses.push(Some(IterFunction {
|
||||
func: IterFunctionKind::Contains(args[0].span),
|
||||
span: expr.span,
|
||||
})),
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, match_type};
|
||||
use clippy_utils::{match_any_def_paths, paths};
|
||||
use clippy_utils::paths;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_ast::ast::LitKind;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -13,7 +13,7 @@ use rustc_span::{Span, sym};
|
|||
use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS};
|
||||
|
||||
fn is_open_options(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
|
||||
is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || match_type(cx, ty, &paths::TOKIO_IO_OPEN_OPTIONS)
|
||||
is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || paths::TOKIO_IO_OPEN_OPTIONS.matches_ty(cx, ty)
|
||||
}
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
|
||||
|
|
@ -126,14 +126,14 @@ fn get_open_options(
|
|||
&& let ExprKind::Path(path) = callee.kind
|
||||
&& let Some(did) = cx.qpath_res(&path, callee.hir_id).opt_def_id()
|
||||
{
|
||||
let std_file_options = [sym::file_options, sym::open_options_new];
|
||||
let is_std_options = matches!(
|
||||
cx.tcx.get_diagnostic_name(did),
|
||||
Some(sym::file_options | sym::open_options_new)
|
||||
);
|
||||
|
||||
let tokio_file_options: &[&[&str]] = &[&paths::TOKIO_IO_OPEN_OPTIONS_NEW, &paths::TOKIO_FILE_OPTIONS];
|
||||
|
||||
let is_std_options = std_file_options
|
||||
.into_iter()
|
||||
.any(|sym| cx.tcx.is_diagnostic_item(sym, did));
|
||||
is_std_options || match_any_def_paths(cx, did, tokio_file_options).is_some()
|
||||
is_std_options
|
||||
|| paths::TOKIO_IO_OPEN_OPTIONS_NEW.matches(cx, did)
|
||||
|| paths::TOKIO_FILE_OPTIONS.matches(cx, did)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
|
|||
use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability};
|
||||
use clippy_utils::ty::get_type_diagnostic_name;
|
||||
use clippy_utils::visitors::for_each_unconsumed_temporary;
|
||||
use clippy_utils::{is_expr_final_block_expr, peel_blocks};
|
||||
use clippy_utils::{get_parent_expr, peel_blocks};
|
||||
|
||||
use super::RETURN_AND_THEN;
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ pub(super) fn check<'tcx>(
|
|||
recv: &'tcx hir::Expr<'tcx>,
|
||||
arg: &'tcx hir::Expr<'_>,
|
||||
) {
|
||||
if !is_expr_final_block_expr(cx.tcx, expr) {
|
||||
if cx.tcx.hir_get_fn_id_for_return_block(expr.hir_id).is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -55,12 +55,24 @@ pub(super) fn check<'tcx>(
|
|||
None => &body_snip,
|
||||
};
|
||||
|
||||
let sugg = format!(
|
||||
"let {} = {}?;\n{}",
|
||||
arg_snip,
|
||||
recv_snip,
|
||||
reindent_multiline(inner, false, indent_of(cx, expr.span))
|
||||
);
|
||||
// If suggestion is going to get inserted as part of a `return` expression, it must be blockified.
|
||||
let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) {
|
||||
let base_indent = indent_of(cx, parent_expr.span);
|
||||
let inner_indent = base_indent.map(|i| i + 4);
|
||||
format!(
|
||||
"{}\n{}\n{}",
|
||||
reindent_multiline(&format!("{{\nlet {arg_snip} = {recv_snip}?;"), true, inner_indent),
|
||||
reindent_multiline(inner, false, inner_indent),
|
||||
reindent_multiline("}", false, base_indent),
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"let {} = {}?;\n{}",
|
||||
arg_snip,
|
||||
recv_snip,
|
||||
reindent_multiline(inner, false, indent_of(cx, expr.span))
|
||||
)
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use clippy_utils::msrvs::{self, Msrv};
|
|||
use clippy_utils::source::snippet_with_context;
|
||||
use clippy_utils::usage::local_used_after_expr;
|
||||
use clippy_utils::visitors::{Descend, for_each_expr};
|
||||
use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths};
|
||||
use clippy_utils::{is_diag_item_method, path_to_local_id, paths};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{
|
||||
|
|
@ -288,7 +288,7 @@ fn parse_iter_usage<'tcx>(
|
|||
match (name.ident.as_str(), args) {
|
||||
("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span),
|
||||
("next_tuple", []) => {
|
||||
return if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE)
|
||||
return if paths::ITERTOOLS_NEXT_TUPLE.matches(cx, did)
|
||||
&& let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind()
|
||||
&& cx.tcx.is_diagnostic_item(sym::Option, adt_def.did())
|
||||
&& let ty::Tuple(subs) = subs.type_at(0).kind()
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ use clippy_utils::{is_trait_method, std_or_core};
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Closure, Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, GenericArgKind};
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_span::sym;
|
||||
use rustc_span::symbol::Ident;
|
||||
use std::iter;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::ty::{implements_trait, should_call_clone_as_function, walk_ptrs_ty_depth};
|
||||
use clippy_utils::{
|
||||
get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, peel_blocks, strip_pat_refs,
|
||||
};
|
||||
use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{self as hir, LangItem};
|
||||
use rustc_lint::LateContext;
|
||||
|
|
@ -81,8 +79,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str,
|
|||
applicability,
|
||||
);
|
||||
}
|
||||
} else if match_def_path(cx, def_id, &["core", "option", "Option", call_name])
|
||||
|| match_def_path(cx, def_id, &["core", "result", "Result", call_name])
|
||||
} else if let Some(impl_id) = cx.tcx.opt_parent(def_id)
|
||||
&& let Some(adt) = cx.tcx.type_of(impl_id).instantiate_identity().ty_adt_def()
|
||||
&& (cx.tcx.lang_items().option_type() == Some(adt.did()) || cx.tcx.is_diagnostic_item(sym::Result, adt.did()))
|
||||
{
|
||||
let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs();
|
||||
let res_ty = cx.typeck_results().expr_ty(expr).peel_refs();
|
||||
|
|
|
|||
|
|
@ -155,9 +155,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
|
|||
return;
|
||||
}
|
||||
|
||||
let mir = cx.tcx.mir_drops_elaborated_and_const_checked(def_id);
|
||||
let mir = cx.tcx.optimized_mir(def_id);
|
||||
|
||||
if let Ok(()) = is_min_const_fn(cx, &mir.borrow(), self.msrv)
|
||||
if let Ok(()) = is_min_const_fn(cx, mir, self.msrv)
|
||||
&& let hir::Node::Item(hir::Item { vis_span, .. }) | hir::Node::ImplItem(hir::ImplItem { vis_span, .. }) =
|
||||
cx.tcx.hir_node_by_def_id(def_id)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -48,6 +48,8 @@ pub struct MissingDoc {
|
|||
/// Whether to **only** check for missing documentation in items visible within the current
|
||||
/// crate. For example, `pub(crate)` items.
|
||||
crate_items_only: bool,
|
||||
/// Whether to allow fields starting with an underscore to skip documentation requirements
|
||||
allow_unused: bool,
|
||||
/// Stack of whether #[doc(hidden)] is set
|
||||
/// at each level which has lint attributes.
|
||||
doc_hidden_stack: Vec<bool>,
|
||||
|
|
@ -59,6 +61,7 @@ impl MissingDoc {
|
|||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
crate_items_only: conf.missing_docs_in_crate_items,
|
||||
allow_unused: conf.missing_docs_allow_unused,
|
||||
doc_hidden_stack: vec![false],
|
||||
prev_span: None,
|
||||
}
|
||||
|
|
@ -260,11 +263,12 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
|
|||
}
|
||||
|
||||
fn check_field_def(&mut self, cx: &LateContext<'tcx>, sf: &'tcx hir::FieldDef<'_>) {
|
||||
if !sf.is_positional() {
|
||||
if !(sf.is_positional()
|
||||
|| is_from_proc_macro(cx, sf)
|
||||
|| self.allow_unused && sf.ident.as_str().starts_with('_'))
|
||||
{
|
||||
let attrs = cx.tcx.hir_attrs(sf.hir_id);
|
||||
if !is_from_proc_macro(cx, sf) {
|
||||
self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field");
|
||||
}
|
||||
self.check_missing_docs_attrs(cx, sf.def_id, attrs, sf.span, "a", "struct field");
|
||||
}
|
||||
self.prev_span = Some(sf.span);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::def_path_def_ids;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::paths::{PathNS, lookup_path_str};
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::Res;
|
||||
|
|
@ -56,8 +56,12 @@ impl ImportRename {
|
|||
renames: conf
|
||||
.enforced_import_renames
|
||||
.iter()
|
||||
.map(|x| (x.path.split("::").collect::<Vec<_>>(), Symbol::intern(&x.rename)))
|
||||
.flat_map(|(path, rename)| def_path_def_ids(tcx, &path).map(move |id| (id, rename)))
|
||||
.map(|x| (&x.path, Symbol::intern(&x.rename)))
|
||||
.flat_map(|(path, rename)| {
|
||||
lookup_path_str(tcx, PathNS::Arbitrary, path)
|
||||
.into_iter()
|
||||
.map(move |id| (id, rename))
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
|
||||
use clippy_utils::sym;
|
||||
use rustc_hir::intravisit::{Visitor, walk_expr};
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -42,10 +43,9 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall {
|
|||
let Some(macro_call) = root_macro_call_first_node(cx, e) else {
|
||||
return;
|
||||
};
|
||||
let macro_name = cx.tcx.item_name(macro_call.def_id);
|
||||
if !matches!(
|
||||
macro_name.as_str(),
|
||||
"debug_assert" | "debug_assert_eq" | "debug_assert_ne"
|
||||
cx.tcx.get_diagnostic_name(macro_call.def_id),
|
||||
Some(sym::debug_assert_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -60,7 +60,10 @@ impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall {
|
|||
cx,
|
||||
DEBUG_ASSERT_WITH_MUT_CALL,
|
||||
span,
|
||||
format!("do not call a function with mutable arguments inside of `{macro_name}!`"),
|
||||
format!(
|
||||
"do not call a function with mutable arguments inside of `{}!`",
|
||||
cx.tcx.item_name(macro_call.def_id)
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -96,10 +99,6 @@ impl<'tcx> Visitor<'tcx> for MutArgVisitor<'_, 'tcx> {
|
|||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::If(..) => {
|
||||
self.found = true;
|
||||
return;
|
||||
},
|
||||
ExprKind::Path(_) => {
|
||||
if let Some(adj) = self.cx.typeck_results().adjustments().get(expr.hir_id)
|
||||
&& adj
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_hir_and_then};
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::paths::{self, PathNS, find_crates, lookup_path_str};
|
||||
use clippy_utils::visitors::for_each_expr;
|
||||
use clippy_utils::{def_path_def_ids, fn_def_id, is_no_std_crate, path_def_id};
|
||||
use clippy_utils::{fn_def_id, is_no_std_crate, path_def_id, sym};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{CrateNum, DefId};
|
||||
use rustc_hir::{self as hir, BodyId, Expr, ExprKind, Item, ItemKind};
|
||||
use rustc_hir::{self as hir, BodyId, Expr, ExprKind, HirId, Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::Span;
|
||||
|
|
@ -62,10 +63,7 @@ static FUNCTION_REPLACEMENTS: &[(&str, Option<&str>)] = &[
|
|||
|
||||
pub struct NonStdLazyStatic {
|
||||
msrv: Msrv,
|
||||
lazy_static_lazy_static: Vec<DefId>,
|
||||
once_cell_crate: Vec<CrateNum>,
|
||||
once_cell_sync_lazy: Vec<DefId>,
|
||||
once_cell_sync_lazy_new: Vec<DefId>,
|
||||
once_cell_crates: Vec<CrateNum>,
|
||||
sugg_map: FxIndexMap<DefId, Option<String>>,
|
||||
lazy_type_defs: FxIndexMap<DefId, LazyInfo>,
|
||||
uses_other_once_cell_types: bool,
|
||||
|
|
@ -76,10 +74,7 @@ impl NonStdLazyStatic {
|
|||
pub fn new(conf: &'static Conf) -> Self {
|
||||
Self {
|
||||
msrv: conf.msrv,
|
||||
lazy_static_lazy_static: Vec::new(),
|
||||
once_cell_crate: Vec::new(),
|
||||
once_cell_sync_lazy: Vec::new(),
|
||||
once_cell_sync_lazy_new: Vec::new(),
|
||||
once_cell_crates: Vec::new(),
|
||||
sugg_map: FxIndexMap::default(),
|
||||
lazy_type_defs: FxIndexMap::default(),
|
||||
uses_other_once_cell_types: false,
|
||||
|
|
@ -95,17 +90,15 @@ fn can_use_lazy_cell(cx: &LateContext<'_>, msrv: Msrv) -> bool {
|
|||
|
||||
impl<'hir> LateLintPass<'hir> for NonStdLazyStatic {
|
||||
fn check_crate(&mut self, cx: &LateContext<'hir>) {
|
||||
// Fetch def_ids for external paths
|
||||
self.lazy_static_lazy_static = def_path_def_ids(cx.tcx, &["lazy_static", "lazy_static"]).collect();
|
||||
self.once_cell_sync_lazy = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy"]).collect();
|
||||
self.once_cell_sync_lazy_new = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy", "new"]).collect();
|
||||
// And CrateNums for `once_cell` crate
|
||||
self.once_cell_crate = self.once_cell_sync_lazy.iter().map(|d| d.krate).collect();
|
||||
// Add CrateNums for `once_cell` crate
|
||||
self.once_cell_crates = find_crates(cx.tcx, sym::once_cell)
|
||||
.iter()
|
||||
.map(|def_id| def_id.krate)
|
||||
.collect();
|
||||
|
||||
// Convert hardcoded fn replacement list into a map with def_id
|
||||
for (path, sugg) in FUNCTION_REPLACEMENTS {
|
||||
let path_vec: Vec<&str> = path.split("::").collect();
|
||||
for did in def_path_def_ids(cx.tcx, &path_vec) {
|
||||
for did in lookup_path_str(cx.tcx, PathNS::Value, path) {
|
||||
self.sugg_map.insert(did, sugg.map(ToOwned::to_owned));
|
||||
}
|
||||
}
|
||||
|
|
@ -114,7 +107,7 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic {
|
|||
fn check_item(&mut self, cx: &LateContext<'hir>, item: &Item<'hir>) {
|
||||
if let ItemKind::Static(..) = item.kind
|
||||
&& let Some(macro_call) = clippy_utils::macros::root_macro_call(item.span)
|
||||
&& self.lazy_static_lazy_static.contains(¯o_call.def_id)
|
||||
&& paths::LAZY_STATIC.matches(cx, macro_call.def_id)
|
||||
&& can_use_lazy_cell(cx, self.msrv)
|
||||
{
|
||||
span_lint(
|
||||
|
|
@ -130,7 +123,7 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic {
|
|||
return;
|
||||
}
|
||||
|
||||
if let Some(lazy_info) = LazyInfo::from_item(self, cx, item)
|
||||
if let Some(lazy_info) = LazyInfo::from_item(cx, item)
|
||||
&& can_use_lazy_cell(cx, self.msrv)
|
||||
{
|
||||
self.lazy_type_defs.insert(item.owner_id.to_def_id(), lazy_info);
|
||||
|
|
@ -155,9 +148,9 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic {
|
|||
if let rustc_hir::TyKind::Path(qpath) = ty.peel_refs().kind
|
||||
&& let Some(ty_def_id) = cx.qpath_res(&qpath, ty.hir_id).opt_def_id()
|
||||
// Is from `once_cell` crate
|
||||
&& self.once_cell_crate.contains(&ty_def_id.krate)
|
||||
&& self.once_cell_crates.contains(&ty_def_id.krate)
|
||||
// And is NOT `once_cell::sync::Lazy`
|
||||
&& !self.once_cell_sync_lazy.contains(&ty_def_id)
|
||||
&& !paths::ONCE_CELL_SYNC_LAZY.matches(cx, ty_def_id)
|
||||
{
|
||||
self.uses_other_once_cell_types = true;
|
||||
}
|
||||
|
|
@ -180,6 +173,8 @@ struct LazyInfo {
|
|||
/// // ^^^^
|
||||
/// ```
|
||||
ty_span_no_args: Span,
|
||||
/// Item on which the lint must be generated.
|
||||
item_hir_id: HirId,
|
||||
/// `Span` and `DefId` of calls on `Lazy` type.
|
||||
/// i.e.:
|
||||
/// ```ignore
|
||||
|
|
@ -190,12 +185,12 @@ struct LazyInfo {
|
|||
}
|
||||
|
||||
impl LazyInfo {
|
||||
fn from_item(state: &NonStdLazyStatic, cx: &LateContext<'_>, item: &Item<'_>) -> Option<Self> {
|
||||
fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Option<Self> {
|
||||
// Check if item is a `once_cell:sync::Lazy` static.
|
||||
if let ItemKind::Static(_, ty, _, body_id) = item.kind
|
||||
&& let Some(path_def_id) = path_def_id(cx, ty)
|
||||
&& let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
|
||||
&& state.once_cell_sync_lazy.contains(&path_def_id)
|
||||
&& paths::ONCE_CELL_SYNC_LAZY.matches(cx, path_def_id)
|
||||
{
|
||||
let ty_span_no_args = path_span_without_args(path);
|
||||
let body = cx.tcx.hir_body(body_id);
|
||||
|
|
@ -204,7 +199,7 @@ impl LazyInfo {
|
|||
let mut new_fn_calls = FxIndexMap::default();
|
||||
for_each_expr::<(), ()>(cx, body, |ex| {
|
||||
if let Some((fn_did, call_span)) = fn_def_id_and_span_from_body(cx, ex, body_id)
|
||||
&& state.once_cell_sync_lazy_new.contains(&fn_did)
|
||||
&& paths::ONCE_CELL_SYNC_LAZY_NEW.matches(cx, fn_did)
|
||||
{
|
||||
new_fn_calls.insert(call_span, fn_did);
|
||||
}
|
||||
|
|
@ -213,6 +208,7 @@ impl LazyInfo {
|
|||
|
||||
Some(LazyInfo {
|
||||
ty_span_no_args,
|
||||
item_hir_id: item.hir_id(),
|
||||
calls_span_and_id: new_fn_calls,
|
||||
})
|
||||
} else {
|
||||
|
|
@ -236,9 +232,10 @@ impl LazyInfo {
|
|||
}
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
span_lint_hir_and_then(
|
||||
cx,
|
||||
NON_STD_LAZY_STATICS,
|
||||
self.item_hir_id,
|
||||
self.ty_span_no_args,
|
||||
"this type has been superseded by `LazyLock` in the standard library",
|
||||
|diag| {
|
||||
|
|
|
|||
|
|
@ -1,20 +1,18 @@
|
|||
use clippy_utils::ast_utils::is_useless_with_eq_exprs;
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
|
||||
use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace};
|
||||
use clippy_utils::{eq_expr_value, is_in_test_function};
|
||||
use clippy_utils::{eq_expr_value, is_in_test_function, sym};
|
||||
use rustc_hir::{BinOpKind, Expr};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use super::EQ_OP;
|
||||
|
||||
pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
||||
if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| {
|
||||
let name = cx.tcx.item_name(macro_call.def_id);
|
||||
if let Some(macro_call) = first_node_macro_backtrace(cx, e).find(|macro_call| {
|
||||
matches!(
|
||||
name.as_str(),
|
||||
"assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne"
|
||||
cx.tcx.get_diagnostic_name(macro_call.def_id),
|
||||
Some(sym::assert_eq_macro | sym::assert_ne_macro | sym::debug_assert_eq_macro | sym::debug_assert_ne_macro)
|
||||
)
|
||||
.then(|| (macro_call, name))
|
||||
}) && let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn)
|
||||
&& eq_expr_value(cx, lhs, rhs)
|
||||
&& macro_call.is_local()
|
||||
|
|
@ -24,7 +22,10 @@ pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
|
|||
cx,
|
||||
EQ_OP,
|
||||
lhs.span.to(rhs.span),
|
||||
format!("identical args used in this `{macro_name}!` macro call"),
|
||||
format!(
|
||||
"identical args used in this `{}!` macro call",
|
||||
cx.tcx.item_name(macro_call.def_id)
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_span::symbol::sym;
|
||||
|
||||
use super::INTEGER_DIVISION;
|
||||
|
||||
|
|
@ -13,7 +15,8 @@ pub(crate) fn check<'tcx>(
|
|||
) {
|
||||
if op == hir::BinOpKind::Div
|
||||
&& cx.typeck_results().expr_ty(left).is_integral()
|
||||
&& cx.typeck_results().expr_ty(right).is_integral()
|
||||
&& let right_ty = cx.typeck_results().expr_ty(right)
|
||||
&& (right_ty.is_integral() || is_type_diagnostic_item(cx, right_ty, sym::NonZero))
|
||||
{
|
||||
#[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
|
||||
span_lint_and_then(cx, INTEGER_DIVISION, expr.span, "integer division", |diag| {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::macros::root_macro_call_first_node;
|
||||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||
use clippy_utils::ty::is_type_diagnostic_item;
|
||||
use clippy_utils::visitors::{Descend, for_each_expr};
|
||||
use clippy_utils::{is_inside_always_const_context, return_ty};
|
||||
|
|
@ -69,10 +69,11 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir
|
|||
return ControlFlow::Continue(Descend::Yes);
|
||||
};
|
||||
if !is_inside_always_const_context(cx.tcx, e.hir_id)
|
||||
&& matches!(
|
||||
cx.tcx.item_name(macro_call.def_id).as_str(),
|
||||
"panic" | "assert" | "assert_eq" | "assert_ne"
|
||||
)
|
||||
&& (is_panic(cx, macro_call.def_id)
|
||||
|| matches!(
|
||||
cx.tcx.get_diagnostic_name(macro_call.def_id),
|
||||
Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro)
|
||||
))
|
||||
{
|
||||
panics.push(macro_call.span);
|
||||
ControlFlow::Continue(Descend::No)
|
||||
|
|
|
|||
|
|
@ -113,8 +113,8 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
|
|||
);
|
||||
return;
|
||||
}
|
||||
match cx.tcx.item_name(macro_call.def_id).as_str() {
|
||||
"todo" => {
|
||||
match cx.tcx.get_diagnostic_name(macro_call.def_id) {
|
||||
Some(sym::todo_macro) => {
|
||||
span_lint(
|
||||
cx,
|
||||
TODO,
|
||||
|
|
@ -122,7 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
|
|||
"`todo` should not be present in production code",
|
||||
);
|
||||
},
|
||||
"unimplemented" => {
|
||||
Some(sym::unimplemented_macro) => {
|
||||
span_lint(
|
||||
cx,
|
||||
UNIMPLEMENTED,
|
||||
|
|
@ -130,7 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
|
|||
"`unimplemented` should not be present in production code",
|
||||
);
|
||||
},
|
||||
"unreachable" => {
|
||||
Some(sym::unreachable_macro) => {
|
||||
span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro");
|
||||
},
|
||||
_ => {},
|
||||
|
|
|
|||
|
|
@ -2,8 +2,9 @@ use std::fmt::Display;
|
|||
|
||||
use clippy_utils::consts::{ConstEvalCtxt, Constant};
|
||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
|
||||
use clippy_utils::paths::PathLookup;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::{def_path_res_with_base, find_crates, path_def_id, paths, sym};
|
||||
use clippy_utils::{path_def_id, paths};
|
||||
use rustc_ast::ast::{LitKind, StrStyle};
|
||||
use rustc_hir::def_id::DefIdMap;
|
||||
use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId};
|
||||
|
|
@ -121,17 +122,9 @@ impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX, REGEX_CREATION_IN_LOOPS]
|
|||
|
||||
impl<'tcx> LateLintPass<'tcx> for Regex {
|
||||
fn check_crate(&mut self, cx: &LateContext<'tcx>) {
|
||||
// We don't use `match_def_path` here because that relies on matching the exact path, which changed
|
||||
// between regex 1.8 and 1.9
|
||||
//
|
||||
// `def_path_res_with_base` will resolve through re-exports but is relatively heavy, so we only
|
||||
// perform the operation once and store the results
|
||||
let regex_crates = find_crates(cx.tcx, sym::regex);
|
||||
let mut resolve = |path: &[&str], kind: RegexKind| {
|
||||
for res in def_path_res_with_base(cx.tcx, regex_crates.clone(), &path[1..]) {
|
||||
if let Some(id) = res.opt_def_id() {
|
||||
self.definitions.insert(id, kind);
|
||||
}
|
||||
let mut resolve = |path: &PathLookup, kind: RegexKind| {
|
||||
for &id in path.get(cx) {
|
||||
self.definitions.insert(id, kind);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use clippy_utils::visitors::for_each_expr;
|
|||
use clippy_utils::{
|
||||
binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor,
|
||||
leaks_droppable_temporary_with_limited_lifetime, path_res, path_to_local_id, span_contains_cfg,
|
||||
span_find_starting_semi,
|
||||
span_find_starting_semi, sym,
|
||||
};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_ast::MetaItemInner;
|
||||
|
|
@ -22,7 +22,7 @@ use rustc_middle::ty::{self, GenericArgKind, Ty};
|
|||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::edition::Edition;
|
||||
use rustc_span::{BytePos, Pos, Span, sym};
|
||||
use rustc_span::{BytePos, Pos, Span};
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Display;
|
||||
|
||||
|
|
@ -411,8 +411,8 @@ fn check_final_expr<'tcx>(
|
|||
&& let [tool, lint_name] = meta_item.path.segments.as_slice()
|
||||
&& tool.ident.name == sym::clippy
|
||||
&& matches!(
|
||||
lint_name.ident.name.as_str(),
|
||||
"needless_return" | "style" | "all" | "warnings"
|
||||
lint_name.ident.name,
|
||||
sym::needless_return | sym::style | sym::all | sym::warnings
|
||||
)
|
||||
{
|
||||
// This is an expectation of the `needless_return` lint
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::{get_trait_def_id, paths};
|
||||
use clippy_utils::paths;
|
||||
use rustc_hir::{Impl, Item, ItemKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
|
|
@ -32,9 +32,7 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi {
|
|||
}) = item.kind
|
||||
{
|
||||
let did = trait_ref.path.res.def_id();
|
||||
if let Some(visit_did) = get_trait_def_id(cx.tcx, &paths::SERDE_DE_VISITOR)
|
||||
&& did == visit_did
|
||||
{
|
||||
if paths::SERDE_DE_VISITOR.matches(cx, did) {
|
||||
let mut seen_str = None;
|
||||
let mut seen_string = None;
|
||||
for item in *items {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ declare_clippy_lint! {
|
|||
/// param * 2
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.86.0"]
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub SINGLE_OPTION_MAP,
|
||||
nursery,
|
||||
"Checks for functions with method calls to `.map(_)` on an arg of type `Option` as the outermost expression."
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs;
|
|||
use clippy_utils::macros::root_macro_call_first_node;
|
||||
use clippy_utils::source::SpanRangeExt;
|
||||
use clippy_utils::ty::implements_trait;
|
||||
use clippy_utils::{get_trait_def_id, is_no_std_crate};
|
||||
use clippy_utils::{is_no_std_crate, paths};
|
||||
use rustc_ast::{LitIntType, LitKind, UintTy};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr};
|
||||
|
|
@ -100,7 +100,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
|
|||
&& let Some(start_snippet) = start.span.get_source_text(cx)
|
||||
&& let Some(end_snippet) = end.span.get_source_text(cx)
|
||||
{
|
||||
let should_emit_every_value = if let Some(step_def_id) = get_trait_def_id(cx.tcx, &["core", "iter", "Step"])
|
||||
let should_emit_every_value = if let Some(step_def_id) = paths::ITER_STEP.only(cx)
|
||||
&& implements_trait(cx, ty, step_def_id, &[])
|
||||
{
|
||||
true
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
|
|||
if let ExprKind::MethodCall(path, recv, [], _) = &e.kind
|
||||
&& path.ident.name == sym::into_bytes
|
||||
&& let ExprKind::MethodCall(path, recv, [], _) = &recv.kind
|
||||
&& matches!(path.ident.name.as_str(), "to_owned" | "to_string")
|
||||
&& matches!(path.ident.name, sym::to_owned | sym::to_string)
|
||||
&& let ExprKind::Lit(lit) = &recv.kind
|
||||
&& let LitKind::Str(lit_content, _) = &lit.node
|
||||
&& lit_content.as_str().is_ascii()
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use clippy_config::Conf;
|
||||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::source::snippet_with_applicability;
|
||||
use clippy_utils::{match_def_path, sym};
|
||||
use clippy_utils::{is_in_const_context, paths, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -33,34 +34,35 @@ declare_clippy_lint! {
|
|||
"`char.is_digit()` is clearer"
|
||||
}
|
||||
|
||||
declare_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]);
|
||||
impl_lint_pass!(ToDigitIsSome => [TO_DIGIT_IS_SOME]);
|
||||
|
||||
pub(crate) struct ToDigitIsSome {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
||||
impl ToDigitIsSome {
|
||||
pub(crate) fn new(conf: &'static Conf) -> Self {
|
||||
Self { msrv: conf.msrv }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
|
||||
if let hir::ExprKind::MethodCall(is_some_path, to_digit_expr, [], _) = &expr.kind
|
||||
&& is_some_path.ident.name == sym::is_some
|
||||
{
|
||||
let match_result = match &to_digit_expr.kind {
|
||||
let match_result = match to_digit_expr.kind {
|
||||
hir::ExprKind::MethodCall(to_digits_path, char_arg, [radix_arg], _) => {
|
||||
if to_digits_path.ident.name == sym::to_digit
|
||||
&& let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg)
|
||||
&& *char_arg_ty.kind() == ty::Char
|
||||
&& cx.typeck_results().expr_ty_adjusted(char_arg).is_char()
|
||||
{
|
||||
Some((true, *char_arg, radix_arg))
|
||||
Some((true, char_arg, radix_arg))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
},
|
||||
hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => {
|
||||
if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind
|
||||
&& let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id)
|
||||
&& let Some(to_digits_def_id) = to_digits_call_res.opt_def_id()
|
||||
&& match_def_path(
|
||||
cx,
|
||||
to_digits_def_id,
|
||||
&["core", "char", "methods", "<impl char>", "to_digit"],
|
||||
)
|
||||
{
|
||||
if paths::CHAR_TO_DIGIT.matches_path(cx, to_digits_call) {
|
||||
Some((false, char_arg, radix_arg))
|
||||
} else {
|
||||
None
|
||||
|
|
@ -69,7 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome {
|
|||
_ => None,
|
||||
};
|
||||
|
||||
if let Some((is_method_call, char_arg, radix_arg)) = match_result {
|
||||
if let Some((is_method_call, char_arg, radix_arg)) = match_result
|
||||
&& (!is_in_const_context(cx) || self.msrv.meets(cx, msrvs::CONST_CHAR_IS_DIGIT))
|
||||
{
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let char_arg_snip = snippet_with_applicability(cx, char_arg.span, "_", &mut applicability);
|
||||
let radix_snip = snippet_with_applicability(cx, radix_arg.span, "_", &mut applicability);
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ use rustc_hir::{
|
|||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::impl_lint_pass;
|
||||
use rustc_span::{BytePos, Span};
|
||||
use rustc_span::Span;
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -282,18 +282,18 @@ impl TraitBounds {
|
|||
.iter()
|
||||
.copied()
|
||||
.chain(p.bounds.iter())
|
||||
.filter_map(get_trait_info_from_bound)
|
||||
.map(|(_, _, span)| snippet_with_applicability(cx, span, "..", &mut applicability))
|
||||
.map(|bound| snippet_with_applicability(cx, bound.span(), "_", &mut applicability))
|
||||
.join(" + ");
|
||||
let hint_string = format!(
|
||||
"consider combining the bounds: `{}: {trait_bounds}`",
|
||||
snippet(cx, p.bounded_ty.span, "_"),
|
||||
);
|
||||
let ty_name = snippet(cx, p.bounded_ty.span, "_");
|
||||
span_lint_and_help(
|
||||
cx,
|
||||
TYPE_REPETITION_IN_BOUNDS,
|
||||
bound.span,
|
||||
"this type has already been used as a bound predicate",
|
||||
format!("type `{ty_name}` has already been used as a bound predicate"),
|
||||
None,
|
||||
hint_string,
|
||||
);
|
||||
|
|
@ -395,15 +395,7 @@ impl Hash for ComparableTraitRef<'_, '_> {
|
|||
fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> {
|
||||
if let GenericBound::Trait(t) = bound {
|
||||
let trait_path = t.trait_ref.path;
|
||||
let trait_span = {
|
||||
let path_span = trait_path.span;
|
||||
if let BoundPolarity::Maybe(_) = t.modifiers.polarity {
|
||||
path_span.with_lo(path_span.lo() - BytePos(1)) // include the `?`
|
||||
} else {
|
||||
path_span
|
||||
}
|
||||
};
|
||||
Some((trait_path.res, trait_path.segments, trait_span))
|
||||
Some((trait_path.res, trait_path.segments, t.span))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::ty::is_normalizable;
|
||||
use clippy_utils::{eq_expr_value, path_to_local, sym};
|
||||
use rustc_abi::WrappingRange;
|
||||
use rustc_errors::Applicability;
|
||||
|
|
@ -84,8 +83,6 @@ pub(super) fn check<'tcx>(
|
|||
&& path.ident.name == sym::then_some
|
||||
&& is_local_with_projections(transmutable)
|
||||
&& binops_with_local(cx, transmutable, receiver)
|
||||
&& is_normalizable(cx, cx.param_env, from_ty)
|
||||
&& is_normalizable(cx, cx.param_env, to_ty)
|
||||
// we only want to lint if the target type has a niche that is larger than the one of the source type
|
||||
// e.g. `u8` to `NonZero<u8>` should lint, but `NonZero<u8>` to `u8` should not
|
||||
&& let Ok(from_layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(from_ty))
|
||||
|
|
|
|||
|
|
@ -1,13 +1,9 @@
|
|||
mod crosspointer_transmute;
|
||||
mod eager_transmute;
|
||||
mod missing_transmute_annotations;
|
||||
mod transmute_float_to_int;
|
||||
mod transmute_int_to_bool;
|
||||
mod transmute_int_to_char;
|
||||
mod transmute_int_to_float;
|
||||
mod transmute_int_to_non_zero;
|
||||
mod transmute_null_to_fn;
|
||||
mod transmute_num_to_bytes;
|
||||
mod transmute_ptr_to_ptr;
|
||||
mod transmute_ptr_to_ref;
|
||||
mod transmute_ref_to_ref;
|
||||
|
|
@ -141,40 +137,6 @@ declare_clippy_lint! {
|
|||
"transmutes from a pointer to a reference type"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for transmutes from an integer to a `char`.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Not every integer is a Unicode scalar value.
|
||||
///
|
||||
/// ### Known problems
|
||||
/// - [`from_u32`] which this lint suggests using is slower than `transmute`
|
||||
/// as it needs to validate the input.
|
||||
/// If you are certain that the input is always a valid Unicode scalar value,
|
||||
/// use [`from_u32_unchecked`] which is as fast as `transmute`
|
||||
/// but has a semantically meaningful name.
|
||||
/// - You might want to handle `None` returned from [`from_u32`] instead of calling `unwrap`.
|
||||
///
|
||||
/// [`from_u32`]: https://doc.rust-lang.org/std/char/fn.from_u32.html
|
||||
/// [`from_u32_unchecked`]: https://doc.rust-lang.org/std/char/fn.from_u32_unchecked.html
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// let x = 1_u32;
|
||||
/// unsafe {
|
||||
/// let _: char = std::mem::transmute(x); // where x: u32
|
||||
/// }
|
||||
///
|
||||
/// // should be:
|
||||
/// let _ = std::char::from_u32(x).unwrap();
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub TRANSMUTE_INT_TO_CHAR,
|
||||
complexity,
|
||||
"transmutes from an integer to a `char`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for transmutes from a `&[u8]` to a `&str`.
|
||||
|
|
@ -232,29 +194,6 @@ declare_clippy_lint! {
|
|||
"transmutes from an integer to a `bool`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for transmutes from an integer to a float.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Transmutes are dangerous and error-prone, whereas `from_bits` is intuitive
|
||||
/// and safe.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// unsafe {
|
||||
/// let _: f32 = std::mem::transmute(1_u32); // where x: u32
|
||||
/// }
|
||||
///
|
||||
/// // should be:
|
||||
/// let _: f32 = f32::from_bits(1_u32);
|
||||
/// ```
|
||||
#[clippy::version = "pre 1.29.0"]
|
||||
pub TRANSMUTE_INT_TO_FLOAT,
|
||||
complexity,
|
||||
"transmutes from an integer to a float"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for transmutes from `T` to `NonZero<T>`, and suggests the `new_unchecked`
|
||||
|
|
@ -280,52 +219,6 @@ declare_clippy_lint! {
|
|||
"transmutes from an integer to a non-zero wrapper"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for transmutes from a float to an integer.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// Transmutes are dangerous and error-prone, whereas `to_bits` is intuitive
|
||||
/// and safe.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// unsafe {
|
||||
/// let _: u32 = std::mem::transmute(1f32);
|
||||
/// }
|
||||
///
|
||||
/// // should be:
|
||||
/// let _: u32 = 1f32.to_bits();
|
||||
/// ```
|
||||
#[clippy::version = "1.41.0"]
|
||||
pub TRANSMUTE_FLOAT_TO_INT,
|
||||
complexity,
|
||||
"transmutes from a float to an integer"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for transmutes from a number to an array of `u8`
|
||||
///
|
||||
/// ### Why this is bad?
|
||||
/// Transmutes are dangerous and error-prone, whereas `to_ne_bytes`
|
||||
/// is intuitive and safe.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// unsafe {
|
||||
/// let x: [u8; 8] = std::mem::transmute(1i64);
|
||||
/// }
|
||||
///
|
||||
/// // should be
|
||||
/// let x: [u8; 8] = 0i64.to_ne_bytes();
|
||||
/// ```
|
||||
#[clippy::version = "1.58.0"]
|
||||
pub TRANSMUTE_NUM_TO_BYTES,
|
||||
complexity,
|
||||
"transmutes from a number to an array of `u8`"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for transmutes from a pointer to a pointer, or
|
||||
|
|
@ -581,13 +474,9 @@ impl_lint_pass!(Transmute => [
|
|||
TRANSMUTE_PTR_TO_PTR,
|
||||
USELESS_TRANSMUTE,
|
||||
WRONG_TRANSMUTE,
|
||||
TRANSMUTE_INT_TO_CHAR,
|
||||
TRANSMUTE_BYTES_TO_STR,
|
||||
TRANSMUTE_INT_TO_BOOL,
|
||||
TRANSMUTE_INT_TO_FLOAT,
|
||||
TRANSMUTE_INT_TO_NON_ZERO,
|
||||
TRANSMUTE_FLOAT_TO_INT,
|
||||
TRANSMUTE_NUM_TO_BYTES,
|
||||
UNSOUND_COLLECTION_TRANSMUTE,
|
||||
TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
|
||||
TRANSMUTE_UNDEFINED_REPR,
|
||||
|
|
@ -632,14 +521,10 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
|
|||
| transmute_null_to_fn::check(cx, e, arg, to_ty)
|
||||
| transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
|
||||
| missing_transmute_annotations::check(cx, path, from_ty, to_ty, e.hir_id)
|
||||
| transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||
| transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
|
||||
| transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, self.msrv)
|
||||
| transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
|
||||
| transmute_int_to_float::check(cx, e, from_ty, to_ty, arg, const_context, self.msrv)
|
||||
| transmute_int_to_non_zero::check(cx, e, from_ty, to_ty, arg)
|
||||
| transmute_float_to_int::check(cx, e, from_ty, to_ty, arg, const_context, self.msrv)
|
||||
| transmute_num_to_bytes::check(cx, e, from_ty, to_ty, arg, const_context, self.msrv)
|
||||
| (unsound_collection_transmute::check(cx, e, from_ty, to_ty)
|
||||
|| transmute_undefined_repr::check(cx, e, from_ty, to_ty))
|
||||
| (eager_transmute::check(cx, e, arg, from_ty, to_ty));
|
||||
|
|
|
|||
|
|
@ -1,66 +0,0 @@
|
|||
use super::TRANSMUTE_FLOAT_TO_INT;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::sugg;
|
||||
use rustc_ast as ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
||||
/// Checks for `transmute_float_to_int` lint.
|
||||
/// Returns `true` if it's triggered, otherwise returns `false`.
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
from_ty: Ty<'tcx>,
|
||||
to_ty: Ty<'tcx>,
|
||||
mut arg: &'tcx Expr<'_>,
|
||||
const_context: bool,
|
||||
msrv: Msrv,
|
||||
) -> bool {
|
||||
match (&from_ty.kind(), &to_ty.kind()) {
|
||||
(ty::Float(float_ty), ty::Int(_) | ty::Uint(_))
|
||||
if !const_context || msrv.meets(cx, msrvs::CONST_FLOAT_BITS_CONV) =>
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_FLOAT_TO_INT,
|
||||
e.span,
|
||||
format!("transmute from a `{from_ty}` to a `{to_ty}`"),
|
||||
|diag| {
|
||||
let mut sugg = sugg::Sugg::hir(cx, arg, "..");
|
||||
|
||||
if let ExprKind::Unary(UnOp::Neg, inner_expr) = &arg.kind {
|
||||
arg = inner_expr;
|
||||
}
|
||||
|
||||
if let ExprKind::Lit(lit) = &arg.kind
|
||||
// if the expression is a float literal and it is unsuffixed then
|
||||
// add a suffix so the suggestion is valid and unambiguous
|
||||
&& let ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) = lit.node
|
||||
{
|
||||
let op = format!("{sugg}{}", float_ty.name_str()).into();
|
||||
match sugg {
|
||||
sugg::Sugg::MaybeParen(_) => sugg = sugg::Sugg::MaybeParen(op),
|
||||
_ => sugg = sugg::Sugg::NonParen(op),
|
||||
}
|
||||
}
|
||||
|
||||
sugg = sugg::Sugg::NonParen(format!("{}.to_bits()", sugg.maybe_paren()).into());
|
||||
|
||||
// cast the result of `to_bits` if `to_ty` is signed
|
||||
sugg = if let ty::Int(int_ty) = to_ty.kind() {
|
||||
sugg.as_ty(int_ty.name_str().to_string())
|
||||
} else {
|
||||
sugg
|
||||
};
|
||||
|
||||
diag.span_suggestion(e.span, "consider using", sugg, Applicability::Unspecified);
|
||||
},
|
||||
);
|
||||
true
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
use super::TRANSMUTE_INT_TO_CHAR;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::{std_or_core, sugg};
|
||||
use rustc_ast as ast;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
||||
/// Checks for `transmute_int_to_char` lint.
|
||||
/// Returns `true` if it's triggered, otherwise returns `false`.
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
from_ty: Ty<'tcx>,
|
||||
to_ty: Ty<'tcx>,
|
||||
arg: &'tcx Expr<'_>,
|
||||
const_context: bool,
|
||||
) -> bool {
|
||||
match (&from_ty.kind(), &to_ty.kind()) {
|
||||
(ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) if !const_context => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_INT_TO_CHAR,
|
||||
e.span,
|
||||
format!("transmute from a `{from_ty}` to a `char`"),
|
||||
|diag| {
|
||||
let Some(top_crate) = std_or_core(cx) else { return };
|
||||
let arg = sugg::Sugg::hir(cx, arg, "..");
|
||||
let arg = if let ty::Int(_) = from_ty.kind() {
|
||||
arg.as_ty(ast::UintTy::U32.name_str())
|
||||
} else {
|
||||
arg
|
||||
};
|
||||
diag.span_suggestion(
|
||||
e.span,
|
||||
"consider using",
|
||||
format!("{top_crate}::char::from_u32({arg}).unwrap()"),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
},
|
||||
);
|
||||
true
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
use super::TRANSMUTE_INT_TO_FLOAT;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::sugg;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
|
||||
/// Checks for `transmute_int_to_float` lint.
|
||||
/// Returns `true` if it's triggered, otherwise returns `false`.
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
from_ty: Ty<'tcx>,
|
||||
to_ty: Ty<'tcx>,
|
||||
arg: &'tcx Expr<'_>,
|
||||
const_context: bool,
|
||||
msrv: Msrv,
|
||||
) -> bool {
|
||||
match (&from_ty.kind(), &to_ty.kind()) {
|
||||
(ty::Int(_) | ty::Uint(_), ty::Float(_)) if !const_context || msrv.meets(cx, msrvs::CONST_FLOAT_BITS_CONV) => {
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_INT_TO_FLOAT,
|
||||
e.span,
|
||||
format!("transmute from a `{from_ty}` to a `{to_ty}`"),
|
||||
|diag| {
|
||||
let arg = sugg::Sugg::hir(cx, arg, "..");
|
||||
let arg = if let ty::Int(int_ty) = from_ty.kind() {
|
||||
arg.as_ty(format!(
|
||||
"u{}",
|
||||
int_ty.bit_width().map_or_else(|| "size".to_string(), |v| v.to_string())
|
||||
))
|
||||
} else {
|
||||
arg
|
||||
};
|
||||
diag.span_suggestion(
|
||||
e.span,
|
||||
"consider using",
|
||||
format!("{to_ty}::from_bits({arg})"),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
},
|
||||
);
|
||||
true
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
use super::TRANSMUTE_NUM_TO_BYTES;
|
||||
use clippy_utils::diagnostics::span_lint_and_then;
|
||||
use clippy_utils::msrvs::{self, Msrv};
|
||||
use clippy_utils::sugg;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::Expr;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, Ty, UintTy};
|
||||
|
||||
/// Checks for `transmute_int_to_float` lint.
|
||||
/// Returns `true` if it's triggered, otherwise returns `false`.
|
||||
pub(super) fn check<'tcx>(
|
||||
cx: &LateContext<'tcx>,
|
||||
e: &'tcx Expr<'_>,
|
||||
from_ty: Ty<'tcx>,
|
||||
to_ty: Ty<'tcx>,
|
||||
arg: &'tcx Expr<'_>,
|
||||
const_context: bool,
|
||||
msrv: Msrv,
|
||||
) -> bool {
|
||||
match (&from_ty.kind(), &to_ty.kind()) {
|
||||
(ty::Int(_) | ty::Uint(_) | ty::Float(_), ty::Array(arr_ty, _)) => {
|
||||
if !matches!(arr_ty.kind(), ty::Uint(UintTy::U8)) {
|
||||
return false;
|
||||
}
|
||||
if matches!(from_ty.kind(), ty::Float(_)) && const_context && !msrv.meets(cx, msrvs::CONST_FLOAT_BITS_CONV)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
TRANSMUTE_NUM_TO_BYTES,
|
||||
e.span,
|
||||
format!("transmute from a `{from_ty}` to a `{to_ty}`"),
|
||||
|diag| {
|
||||
let arg = sugg::Sugg::hir(cx, arg, "..");
|
||||
diag.span_suggestion(
|
||||
e.span,
|
||||
"consider using `to_ne_bytes()`",
|
||||
format!("{arg}.to_ne_bytes()"),
|
||||
Applicability::Unspecified,
|
||||
);
|
||||
},
|
||||
);
|
||||
true
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
@ -385,7 +385,7 @@ declare_clippy_lint! {
|
|||
/// ```no_run
|
||||
/// let right: std::borrow::Cow<'_, [u8]>;
|
||||
/// ```
|
||||
#[clippy::version = "1.85.0"]
|
||||
#[clippy::version = "1.87.0"]
|
||||
pub OWNED_COW,
|
||||
style,
|
||||
"needlessly owned Cow type"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint;
|
||||
use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
|
||||
use clippy_utils::sym;
|
||||
use rustc_hir::{BinOpKind, Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
|
|
@ -7,11 +8,12 @@ use super::UNIT_CMP;
|
|||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
||||
if expr.span.from_expansion() {
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr) {
|
||||
let macro_name = cx.tcx.item_name(macro_call.def_id);
|
||||
let result = match macro_name.as_str() {
|
||||
"assert_eq" | "debug_assert_eq" => "succeed",
|
||||
"assert_ne" | "debug_assert_ne" => "fail",
|
||||
if let Some(macro_call) = root_macro_call_first_node(cx, expr)
|
||||
&& let Some(diag_name) = cx.tcx.get_diagnostic_name(macro_call.def_id)
|
||||
{
|
||||
let result = match diag_name {
|
||||
sym::assert_eq_macro | sym::debug_assert_eq_macro => "succeed",
|
||||
sym::assert_ne_macro | sym::debug_assert_ne_macro => "fail",
|
||||
_ => return,
|
||||
};
|
||||
let Some((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else {
|
||||
|
|
@ -24,7 +26,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
|
|||
cx,
|
||||
UNIT_CMP,
|
||||
macro_call.span,
|
||||
format!("`{macro_name}` of unit values detected. This will always {result}"),
|
||||
format!(
|
||||
"`{}` of unit values detected. This will always {result}",
|
||||
cx.tcx.item_name(macro_call.def_id)
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then;
|
|||
use clippy_utils::is_def_id_trait_method;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn};
|
||||
use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, Node, YieldSource};
|
||||
use rustc_hir::{Body, Defaultness, Expr, ExprKind, FnDecl, HirId, Node, TraitItem, YieldSource};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_session::impl_lint_pass;
|
||||
|
|
@ -116,7 +116,11 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
|
|||
span: Span,
|
||||
def_id: LocalDefId,
|
||||
) {
|
||||
if !span.from_expansion() && fn_kind.asyncness().is_async() && !is_def_id_trait_method(cx, def_id) {
|
||||
if !span.from_expansion()
|
||||
&& fn_kind.asyncness().is_async()
|
||||
&& !is_def_id_trait_method(cx, def_id)
|
||||
&& !is_default_trait_impl(cx, def_id)
|
||||
{
|
||||
let mut visitor = AsyncFnVisitor {
|
||||
cx,
|
||||
found_await: false,
|
||||
|
|
@ -189,3 +193,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_default_trait_impl(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
|
||||
matches!(
|
||||
cx.tcx.hir_node_by_def_id(def_id),
|
||||
Node::TraitItem(TraitItem {
|
||||
defaultness: Defaultness::Default { .. },
|
||||
..
|
||||
})
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use clippy_utils::diagnostics::span_lint_hir_and_then;
|
||||
use clippy_utils::macros::{is_panic, root_macro_call_first_node};
|
||||
use clippy_utils::{is_res_lang_ctor, is_trait_method, match_def_path, match_trait_method, paths, peel_blocks};
|
||||
use clippy_utils::{is_res_lang_ctor, paths, peel_blocks};
|
||||
use hir::{ExprKind, HirId, PatKind};
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -93,14 +93,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
|
|||
return;
|
||||
}
|
||||
|
||||
let async_paths: [&[&str]; 4] = [
|
||||
let async_paths = [
|
||||
&paths::TOKIO_IO_ASYNCREADEXT,
|
||||
&paths::TOKIO_IO_ASYNCWRITEEXT,
|
||||
&paths::FUTURES_IO_ASYNCREADEXT,
|
||||
&paths::FUTURES_IO_ASYNCWRITEEXT,
|
||||
];
|
||||
|
||||
if async_paths.into_iter().any(|path| match_def_path(cx, trait_id, path)) {
|
||||
if async_paths.into_iter().any(|path| path.matches(cx, trait_id)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -226,7 +226,7 @@ fn is_unreachable_or_panic(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
|
|||
if is_panic(cx, macro_call.def_id) {
|
||||
return !cx.tcx.hir_is_inside_const_context(expr.hir_id);
|
||||
}
|
||||
matches!(cx.tcx.item_name(macro_call.def_id).as_str(), "unreachable")
|
||||
cx.tcx.is_diagnostic_item(sym::unreachable_macro, macro_call.def_id)
|
||||
}
|
||||
|
||||
fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
|
||||
|
|
@ -291,19 +291,28 @@ fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option<IoOp> {
|
|||
},
|
||||
};
|
||||
|
||||
match (
|
||||
is_trait_method(cx, call, sym::IoRead),
|
||||
is_trait_method(cx, call, sym::IoWrite),
|
||||
match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT)
|
||||
|| match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT),
|
||||
match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT)
|
||||
|| match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT),
|
||||
) {
|
||||
(true, _, _, _) => Some(IoOp::SyncRead(vectorized)),
|
||||
(_, true, _, _) => Some(IoOp::SyncWrite(vectorized)),
|
||||
(_, _, true, _) => Some(IoOp::AsyncRead(vectorized)),
|
||||
(_, _, _, true) => Some(IoOp::AsyncWrite(vectorized)),
|
||||
_ => None,
|
||||
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(call.hir_id)
|
||||
&& let Some(trait_def_id) = cx.tcx.trait_of_item(method_def_id)
|
||||
{
|
||||
if let Some(diag_name) = cx.tcx.get_diagnostic_name(trait_def_id) {
|
||||
match diag_name {
|
||||
sym::IoRead => Some(IoOp::SyncRead(vectorized)),
|
||||
sym::IoWrite => Some(IoOp::SyncWrite(vectorized)),
|
||||
_ => None,
|
||||
}
|
||||
} else if paths::FUTURES_IO_ASYNCREADEXT.matches(cx, trait_def_id)
|
||||
|| paths::TOKIO_IO_ASYNCREADEXT.matches(cx, trait_def_id)
|
||||
{
|
||||
Some(IoOp::AsyncRead(vectorized))
|
||||
} else if paths::TOKIO_IO_ASYNCWRITEEXT.matches(cx, trait_def_id)
|
||||
|| paths::FUTURES_IO_ASYNCWRITEEXT.matches(cx, trait_def_id)
|
||||
{
|
||||
Some(IoOp::AsyncWrite(vectorized))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,15 +4,15 @@ use clippy_utils::usage::is_potentially_local_place;
|
|||
use clippy_utils::{higher, path_to_local, sym};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn};
|
||||
use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, PathSegment, UnOp};
|
||||
use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Node, UnOp};
|
||||
use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceWithHirId};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::mir::FakeReadCause;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
|
|
@ -111,7 +111,7 @@ struct UnwrapInfo<'tcx> {
|
|||
/// The check, like `x.is_ok()`
|
||||
check: &'tcx Expr<'tcx>,
|
||||
/// The check's name, like `is_ok`
|
||||
check_name: &'tcx PathSegment<'tcx>,
|
||||
check_name: Symbol,
|
||||
/// The branch where the check takes place, like `if x.is_ok() { .. }`
|
||||
branch: &'tcx Expr<'tcx>,
|
||||
/// Whether `is_some()` or `is_ok()` was called (as opposed to `is_err()` or `is_none()`).
|
||||
|
|
@ -133,12 +133,12 @@ fn collect_unwrap_info<'tcx>(
|
|||
invert: bool,
|
||||
is_entire_condition: bool,
|
||||
) -> Vec<UnwrapInfo<'tcx>> {
|
||||
fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool {
|
||||
is_type_diagnostic_item(cx, ty, sym::Option) && ["is_some", "is_none"].contains(&method_name)
|
||||
fn is_relevant_option_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool {
|
||||
is_type_diagnostic_item(cx, ty, sym::Option) && matches!(method_name, sym::is_none | sym::is_some)
|
||||
}
|
||||
|
||||
fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: &str) -> bool {
|
||||
is_type_diagnostic_item(cx, ty, sym::Result) && ["is_ok", "is_err"].contains(&method_name)
|
||||
fn is_relevant_result_call(cx: &LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> bool {
|
||||
is_type_diagnostic_item(cx, ty, sym::Result) && matches!(method_name, sym::is_err | sym::is_ok)
|
||||
}
|
||||
|
||||
if let ExprKind::Binary(op, left, right) = &expr.kind {
|
||||
|
|
@ -155,14 +155,10 @@ fn collect_unwrap_info<'tcx>(
|
|||
} else if let ExprKind::MethodCall(method_name, receiver, [], _) = &expr.kind
|
||||
&& let Some(local_id) = path_to_local(receiver)
|
||||
&& let ty = cx.typeck_results().expr_ty(receiver)
|
||||
&& let name = method_name.ident.as_str()
|
||||
&& let name = method_name.ident.name
|
||||
&& (is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name))
|
||||
{
|
||||
let unwrappable = match name {
|
||||
"is_some" | "is_ok" => true,
|
||||
"is_err" | "is_none" => false,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let unwrappable = matches!(name, sym::is_some | sym::is_ok);
|
||||
let safe_to_unwrap = unwrappable != invert;
|
||||
let kind = if is_type_diagnostic_item(cx, ty, sym::Option) {
|
||||
UnwrappableKind::Option
|
||||
|
|
@ -174,7 +170,7 @@ fn collect_unwrap_info<'tcx>(
|
|||
local_id,
|
||||
if_expr,
|
||||
check: expr,
|
||||
check_name: method_name,
|
||||
check_name: name,
|
||||
branch,
|
||||
safe_to_unwrap,
|
||||
kind,
|
||||
|
|
@ -184,12 +180,12 @@ fn collect_unwrap_info<'tcx>(
|
|||
Vec::new()
|
||||
}
|
||||
|
||||
/// A HIR visitor delegate that checks if a local variable of type `Option<_>` is mutated,
|
||||
/// *except* for if `Option::as_mut` is called.
|
||||
/// A HIR visitor delegate that checks if a local variable of type `Option` or `Result` is mutated,
|
||||
/// *except* for if `.as_mut()` is called.
|
||||
/// The reason for why we allow that one specifically is that `.as_mut()` cannot change
|
||||
/// the option to `None`, and that is important because this lint relies on the fact that
|
||||
/// the variant, and that is important because this lint relies on the fact that
|
||||
/// `is_some` + `unwrap` is equivalent to `if let Some(..) = ..`, which it would not be if
|
||||
/// the option is changed to None between `is_some` and `unwrap`.
|
||||
/// the option is changed to None between `is_some` and `unwrap`, ditto for `Result`.
|
||||
/// (And also `.as_mut()` is a somewhat common method that is still worth linting on.)
|
||||
struct MutationVisitor<'tcx> {
|
||||
is_mutated: bool,
|
||||
|
|
@ -198,13 +194,13 @@ struct MutationVisitor<'tcx> {
|
|||
}
|
||||
|
||||
/// Checks if the parent of the expression pointed at by the given `HirId` is a call to
|
||||
/// `Option::as_mut`.
|
||||
/// `.as_mut()`.
|
||||
///
|
||||
/// Used by the mutation visitor to specifically allow `.as_mut()` calls.
|
||||
/// In particular, the `HirId` that the visitor receives is the id of the local expression
|
||||
/// (i.e. the `x` in `x.as_mut()`), and that is the reason for why we care about its parent
|
||||
/// expression: that will be where the actual method call is.
|
||||
fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool {
|
||||
fn is_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool {
|
||||
if let Node::Expr(mutating_expr) = tcx.parent_hir_node(expr_id)
|
||||
&& let ExprKind::MethodCall(path, _, [], _) = mutating_expr.kind
|
||||
{
|
||||
|
|
@ -218,14 +214,16 @@ impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> {
|
|||
fn borrow(&mut self, cat: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) {
|
||||
if let ty::BorrowKind::Mutable = bk
|
||||
&& is_potentially_local_place(self.local_id, &cat.place)
|
||||
&& !is_option_as_mut_use(self.tcx, diag_expr_id)
|
||||
&& !is_as_mut_use(self.tcx, diag_expr_id)
|
||||
{
|
||||
self.is_mutated = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn mutate(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {
|
||||
self.is_mutated = true;
|
||||
fn mutate(&mut self, cat: &PlaceWithHirId<'tcx>, _: HirId) {
|
||||
if is_potentially_local_place(self.local_id, &cat.place) {
|
||||
self.is_mutated = true;
|
||||
}
|
||||
}
|
||||
|
||||
fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
|
||||
|
|
@ -276,12 +274,10 @@ enum AsRefKind {
|
|||
/// If it isn't, the expression itself is returned.
|
||||
fn consume_option_as_ref<'tcx>(expr: &'tcx Expr<'tcx>) -> (&'tcx Expr<'tcx>, Option<AsRefKind>) {
|
||||
if let ExprKind::MethodCall(path, recv, [], _) = expr.kind {
|
||||
if path.ident.name == sym::as_ref {
|
||||
(recv, Some(AsRefKind::AsRef))
|
||||
} else if path.ident.name == sym::as_mut {
|
||||
(recv, Some(AsRefKind::AsMut))
|
||||
} else {
|
||||
(expr, None)
|
||||
match path.ident.name {
|
||||
sym::as_ref => (recv, Some(AsRefKind::AsRef)),
|
||||
sym::as_mut => (recv, Some(AsRefKind::AsMut)),
|
||||
_ => (expr, None),
|
||||
}
|
||||
} else {
|
||||
(expr, None)
|
||||
|
|
@ -296,6 +292,10 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> {
|
|||
if expr.span.in_external_macro(self.cx.tcx.sess.source_map()) {
|
||||
return;
|
||||
}
|
||||
// Skip checking inside closures since they are visited through `Unwrap::check_fn()` already.
|
||||
if matches!(expr.kind, ExprKind::Closure(_)) {
|
||||
return;
|
||||
}
|
||||
if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) {
|
||||
walk_expr(self, cond);
|
||||
self.visit_branch(expr, cond, then, false);
|
||||
|
|
@ -332,8 +332,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> {
|
|||
expr.span,
|
||||
format!(
|
||||
"called `{}` on `{unwrappable_variable_name}` after checking its variant with `{}`",
|
||||
method_name.ident.name,
|
||||
unwrappable.check_name.ident.as_str(),
|
||||
method_name.ident.name, unwrappable.check_name,
|
||||
),
|
||||
|diag| {
|
||||
if is_entire_condition {
|
||||
|
|
|
|||
|
|
@ -1,16 +1,18 @@
|
|||
use clippy_utils::{get_attr, higher};
|
||||
use clippy_utils::{MaybePath, get_attr, higher, path_def_id};
|
||||
use itertools::Itertools;
|
||||
use rustc_ast::LitIntType;
|
||||
use rustc_ast::ast::{LitFloatType, LitKind};
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{
|
||||
self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind,
|
||||
FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, TyKind,
|
||||
FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::symbol::{Ident, Symbol};
|
||||
use std::cell::Cell;
|
||||
use std::fmt::{Display, Formatter, Write as _};
|
||||
use std::fmt::{Display, Formatter};
|
||||
|
||||
declare_lint_pass!(
|
||||
/// ### What it does
|
||||
|
|
@ -148,6 +150,15 @@ fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_,
|
|||
}
|
||||
}
|
||||
|
||||
fn paths_static_name(cx: &LateContext<'_>, id: DefId) -> String {
|
||||
cx.get_def_path(id)
|
||||
.iter()
|
||||
.map(Symbol::as_str)
|
||||
.filter(|s| !s.starts_with('<'))
|
||||
.join("_")
|
||||
.to_uppercase()
|
||||
}
|
||||
|
||||
struct Binding<T> {
|
||||
name: String,
|
||||
value: T,
|
||||
|
|
@ -257,11 +268,44 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str());
|
||||
}
|
||||
|
||||
fn qpath(&self, qpath: &Binding<&QPath<'_>>) {
|
||||
fn qpath<'p>(&self, qpath: &Binding<&QPath<'_>>, has_hir_id: &Binding<&impl MaybePath<'p>>) {
|
||||
if let QPath::LangItem(lang_item, ..) = *qpath.value {
|
||||
chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))");
|
||||
} else if let Ok(path) = path_to_string(qpath.value) {
|
||||
chain!(self, "match_qpath({qpath}, &[{}])", path);
|
||||
} else if let Some(def_id) = self.cx.qpath_res(qpath.value, has_hir_id.value.hir_id()).opt_def_id()
|
||||
&& !def_id.is_local()
|
||||
{
|
||||
bind!(self, def_id);
|
||||
chain!(
|
||||
self,
|
||||
"let Some({def_id}) = cx.qpath_res({qpath}, {has_hir_id}.hir_id).opt_def_id()"
|
||||
);
|
||||
if let Some(name) = self.cx.tcx.get_diagnostic_name(def_id.value) {
|
||||
chain!(self, "cx.tcx.is_diagnostic_item(sym::{name}, {def_id})");
|
||||
} else {
|
||||
chain!(
|
||||
self,
|
||||
"paths::{}.matches(cx, {def_id}) // Add the path to `clippy_utils::paths` if needed",
|
||||
paths_static_name(self.cx, def_id.value)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_path<'p>(&self, path: &Binding<&impl MaybePath<'p>>) {
|
||||
if let Some(id) = path_def_id(self.cx, path.value)
|
||||
&& !id.is_local()
|
||||
{
|
||||
if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) {
|
||||
chain!(self, "is_path_lang_item(cx, {path}, LangItem::{}", lang.name());
|
||||
} else if let Some(name) = self.cx.tcx.get_diagnostic_name(id) {
|
||||
chain!(self, "is_path_diagnostic_item(cx, {path}, sym::{name})");
|
||||
} else {
|
||||
chain!(
|
||||
self,
|
||||
"paths::{}.matches_path(cx, {path}) // Add the path to `clippy_utils::paths` if needed",
|
||||
paths_static_name(self.cx, id)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -270,7 +314,6 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
ConstArgKind::Path(ref qpath) => {
|
||||
bind!(self, qpath);
|
||||
chain!(self, "let ConstArgKind::Path(ref {qpath}) = {const_arg}.kind");
|
||||
self.qpath(qpath);
|
||||
},
|
||||
ConstArgKind::Anon(anon_const) => {
|
||||
bind!(self, anon_const);
|
||||
|
|
@ -394,12 +437,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
bind!(self, let_expr);
|
||||
kind!("Let({let_expr})");
|
||||
self.pat(field!(let_expr.pat));
|
||||
// Does what ExprKind::Cast does, only adds a clause for the type
|
||||
// if it's a path
|
||||
if let Some(TyKind::Path(qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) {
|
||||
bind!(self, qpath);
|
||||
chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind");
|
||||
self.qpath(qpath);
|
||||
if let Some(ty) = let_expr.value.ty {
|
||||
bind!(self, ty);
|
||||
chain!(self, "let Some({ty}) = {let_expr}.ty");
|
||||
self.maybe_path(ty);
|
||||
}
|
||||
self.expr(field!(let_expr.init));
|
||||
},
|
||||
|
|
@ -451,11 +492,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
ExprKind::Cast(expr, cast_ty) => {
|
||||
bind!(self, expr, cast_ty);
|
||||
kind!("Cast({expr}, {cast_ty})");
|
||||
if let TyKind::Path(ref qpath) = cast_ty.value.kind {
|
||||
bind!(self, qpath);
|
||||
chain!(self, "let TyKind::Path(ref {qpath}) = {cast_ty}.kind");
|
||||
self.qpath(qpath);
|
||||
}
|
||||
self.maybe_path(cast_ty);
|
||||
self.expr(expr);
|
||||
},
|
||||
ExprKind::Type(expr, _ty) => {
|
||||
|
|
@ -561,10 +598,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
self.expr(object);
|
||||
self.expr(index);
|
||||
},
|
||||
ExprKind::Path(ref qpath) => {
|
||||
bind!(self, qpath);
|
||||
kind!("Path(ref {qpath})");
|
||||
self.qpath(qpath);
|
||||
ExprKind::Path(_) => {
|
||||
self.maybe_path(expr);
|
||||
},
|
||||
ExprKind::AddrOf(kind, mutability, inner) => {
|
||||
bind!(self, inner);
|
||||
|
|
@ -608,7 +643,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
StructTailExpr::None | StructTailExpr::DefaultFields(_) => None,
|
||||
});
|
||||
kind!("Struct({qpath}, {fields}, {base})");
|
||||
self.qpath(qpath);
|
||||
self.qpath(qpath, expr);
|
||||
self.slice(fields, |field| {
|
||||
self.ident(field!(field.ident));
|
||||
self.expr(field!(field.expr));
|
||||
|
|
@ -648,7 +683,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
self.expr(expr);
|
||||
}
|
||||
|
||||
fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>) {
|
||||
fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>, pat: &Binding<&hir::Pat<'_>>) {
|
||||
let kind = |kind| chain!(self, "let PatExprKind::{kind} = {lit}.kind");
|
||||
macro_rules! kind {
|
||||
($($t:tt)*) => (kind(format_args!($($t)*)));
|
||||
|
|
@ -657,15 +692,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
PatExprKind::Lit { lit, negated } => {
|
||||
bind!(self, lit);
|
||||
bind!(self, negated);
|
||||
kind!("Lit{{ref {lit}, {negated} }}");
|
||||
kind!("Lit {{ ref {lit}, {negated} }}");
|
||||
self.lit(lit);
|
||||
},
|
||||
PatExprKind::ConstBlock(_) => kind!("ConstBlock(_)"),
|
||||
PatExprKind::Path(ref qpath) => {
|
||||
bind!(self, qpath);
|
||||
kind!("Path(ref {qpath})");
|
||||
self.qpath(qpath);
|
||||
},
|
||||
PatExprKind::Path(_) => self.maybe_path(pat),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -697,7 +728,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
PatKind::Struct(ref qpath, fields, ignore) => {
|
||||
bind!(self, qpath, fields);
|
||||
kind!("Struct(ref {qpath}, {fields}, {ignore})");
|
||||
self.qpath(qpath);
|
||||
self.qpath(qpath, pat);
|
||||
self.slice(fields, |field| {
|
||||
self.ident(field!(field.ident));
|
||||
self.pat(field!(field.pat));
|
||||
|
|
@ -711,7 +742,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
PatKind::TupleStruct(ref qpath, fields, skip_pos) => {
|
||||
bind!(self, qpath, fields);
|
||||
kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})");
|
||||
self.qpath(qpath);
|
||||
self.qpath(qpath, pat);
|
||||
self.slice(fields, |pat| self.pat(pat));
|
||||
},
|
||||
PatKind::Tuple(fields, skip_pos) => {
|
||||
|
|
@ -743,13 +774,13 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
|||
PatKind::Expr(lit_expr) => {
|
||||
bind!(self, lit_expr);
|
||||
kind!("Expr({lit_expr})");
|
||||
self.pat_expr(lit_expr);
|
||||
self.pat_expr(lit_expr, pat);
|
||||
},
|
||||
PatKind::Range(start, end, end_kind) => {
|
||||
opt_bind!(self, start, end);
|
||||
kind!("Range({start}, {end}, RangeEnd::{end_kind:?})");
|
||||
start.if_some(|e| self.pat_expr(e));
|
||||
end.if_some(|e| self.pat_expr(e));
|
||||
start.if_some(|e| self.pat_expr(e, pat));
|
||||
end.if_some(|e| self.pat_expr(e, pat));
|
||||
},
|
||||
PatKind::Slice(start, middle, end) => {
|
||||
bind!(self, start, end);
|
||||
|
|
@ -797,32 +828,3 @@ fn has_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
|
|||
let attrs = cx.tcx.hir_attrs(hir_id);
|
||||
get_attr(cx.sess(), attrs, "author").count() > 0
|
||||
}
|
||||
|
||||
fn path_to_string(path: &QPath<'_>) -> Result<String, ()> {
|
||||
fn inner(s: &mut String, path: &QPath<'_>) -> Result<(), ()> {
|
||||
match *path {
|
||||
QPath::Resolved(_, path) => {
|
||||
for (i, segment) in path.segments.iter().enumerate() {
|
||||
if i > 0 {
|
||||
*s += ", ";
|
||||
}
|
||||
write!(s, "{:?}", segment.ident.as_str()).unwrap();
|
||||
}
|
||||
},
|
||||
QPath::TypeRelative(ty, segment) => match &ty.kind {
|
||||
TyKind::Path(inner_path) => {
|
||||
inner(s, inner_path)?;
|
||||
*s += ", ";
|
||||
write!(s, "{:?}", segment.ident.as_str()).unwrap();
|
||||
},
|
||||
other => write!(s, "/* unimplemented: {other:?}*/").unwrap(),
|
||||
},
|
||||
QPath::LangItem(..) => return Err(()),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
let mut s = String::new();
|
||||
inner(&mut s, path)?;
|
||||
Ok(s)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -498,9 +498,9 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
|
|||
None => return,
|
||||
},
|
||||
LitKind::Char => (
|
||||
match lit.symbol.as_str() {
|
||||
"\"" => "\\\"",
|
||||
"\\'" => "'",
|
||||
match lit.symbol {
|
||||
sym::DOUBLE_QUOTE => "\\\"",
|
||||
sym::BACKSLASH_SINGLE_QUOTE => "'",
|
||||
_ => match value_string.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
|
||||
Some(stripped) => stripped,
|
||||
None => return,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item, ty_from_hir_ty};
|
||||
use clippy_utils::ty::{is_type_diagnostic_item, ty_from_hir_ty};
|
||||
use rustc_hir::{self as hir, AmbigArg, HirId, ItemKind, Node};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::ty::layout::LayoutOf as _;
|
||||
|
|
@ -54,8 +54,6 @@ impl LateLintPass<'_> for ZeroSizedMapValues {
|
|||
// Fixes https://github.com/rust-lang/rust-clippy/issues/7447 because of
|
||||
// https://github.com/rust-lang/rust/blob/master/compiler/rustc_middle/src/ty/sty.rs#L968
|
||||
&& !ty.has_escaping_bound_vars()
|
||||
// Do this to prevent `layout_of` crashing, being unable to fully normalize `ty`.
|
||||
&& is_normalizable(cx, cx.param_env, ty)
|
||||
&& let Ok(layout) = cx.layout_of(ty)
|
||||
&& layout.is_zst()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue