diff options
Diffstat (limited to 'clippy_lints/src')
106 files changed, 1156 insertions, 737 deletions
diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs index b7f414742f1..c82837746bd 100644 --- a/clippy_lints/src/assertions_on_constants.rs +++ b/clippy_lints/src/assertions_on_constants.rs @@ -1,12 +1,10 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::higher; -use clippy_utils::source::snippet_opt; -use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call, peel_blocks}; -use if_chain::if_chain; -use rustc_hir::{Expr, ExprKind, UnOp}; +use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn}; +use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -36,107 +34,39 @@ declare_lint_pass!(AssertionsOnConstants => [ASSERTIONS_ON_CONSTANTS]); impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - let lint_true = |is_debug: bool| { - span_lint_and_help( - cx, - ASSERTIONS_ON_CONSTANTS, - e.span, - if is_debug { - "`debug_assert!(true)` will be optimized out by the compiler" - } else { - "`assert!(true)` will be optimized out by the compiler" - }, - None, - "remove it", - ); + let Some(macro_call) = root_macro_call_first_node(cx, e) else { return }; + let is_debug = match cx.tcx.get_diagnostic_name(macro_call.def_id) { + Some(sym::debug_assert_macro) => true, + Some(sym::assert_macro) => false, + _ => return, }; - let lint_false_without_message = || { + let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else { return }; + let Some((Constant::Bool(val), _)) = constant(cx, cx.typeck_results(), condition) else { return }; + if val { span_lint_and_help( cx, ASSERTIONS_ON_CONSTANTS, - e.span, - "`assert!(false)` should probably be replaced", + macro_call.span, + &format!( + "`{}!(true)` will be optimized out by the compiler", + cx.tcx.item_name(macro_call.def_id) + ), None, - "use `panic!()` or `unreachable!()`", + "remove it", ); - }; - let lint_false_with_message = |panic_message: String| { + } else if !is_debug { + let (assert_arg, panic_arg) = match panic_expn { + PanicExpn::Empty => ("", ""), + _ => (", ..", ".."), + }; span_lint_and_help( cx, ASSERTIONS_ON_CONSTANTS, - e.span, - &format!("`assert!(false, {})` should probably be replaced", panic_message), + macro_call.span, + &format!("`assert!(false{})` should probably be replaced", assert_arg), None, - &format!("use `panic!({})` or `unreachable!({})`", panic_message, panic_message), + &format!("use `panic!({})` or `unreachable!({0})`", panic_arg), ); - }; - - if let Some(debug_assert_span) = is_expn_of(e.span, "debug_assert") { - if debug_assert_span.from_expansion() { - return; - } - if_chain! { - if let ExprKind::Unary(_, lit) = e.kind; - if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), lit); - if is_true; - then { - lint_true(true); - } - }; - } else if let Some(assert_span) = is_direct_expn_of(e.span, "assert") { - if assert_span.from_expansion() { - return; - } - if let Some(assert_match) = match_assert_with_message(cx, e) { - match assert_match { - // matched assert but not message - AssertKind::WithoutMessage(false) => lint_false_without_message(), - AssertKind::WithoutMessage(true) | AssertKind::WithMessage(_, true) => lint_true(false), - AssertKind::WithMessage(panic_message, false) => lint_false_with_message(panic_message), - }; - } - } - } -} - -/// Result of calling `match_assert_with_message`. -enum AssertKind { - WithMessage(String, bool), - WithoutMessage(bool), -} - -/// Check if the expression matches -/// -/// ```rust,ignore -/// if !c { -/// { -/// ::std::rt::begin_panic(message, _) -/// } -/// } -/// ``` -/// -/// where `message` is any expression and `c` is a constant bool. -fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<AssertKind> { - if_chain! { - if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr); - if let ExprKind::Unary(UnOp::Not, expr) = cond.kind; - // bind the first argument of the `assert!` macro - if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr); - let begin_panic_call = peel_blocks(then); - // function call - if let Some(arg) = match_panic_call(cx, begin_panic_call); - // bind the second argument of the `assert!` macro if it exists - if let panic_message = snippet_opt(cx, arg.span); - // second argument of begin_panic is irrelevant - // as is the second match arm - then { - // an empty message occurs when it was generated by the macro - // (and not passed by the user) - return panic_message - .filter(|msg| !msg.is_empty()) - .map(|msg| AssertKind::WithMessage(msg, is_true)) - .or(Some(AssertKind::WithoutMessage(is_true))); } } - None } diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 0629674307b..a58d12ddd6b 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -1,9 +1,10 @@ //! checks for attributes use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::macros::{is_panic, macro_backtrace}; use clippy_utils::msrvs; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; -use clippy_utils::{extract_msrv_attr, match_panic_def_id, meets_msrv}; +use clippy_utils::{extract_msrv_attr, meets_msrv}; use if_chain::if_chain; use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem}; use rustc_errors::Applicability; @@ -443,20 +444,15 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_ } fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool { + if macro_backtrace(expr.span).last().map_or(false, |macro_call| { + is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable + }) { + return false; + } match &expr.kind { ExprKind::Block(block, _) => is_relevant_block(cx, typeck_results, block), ExprKind::Ret(Some(e)) => is_relevant_expr(cx, typeck_results, e), ExprKind::Ret(None) | ExprKind::Break(_, None) => false, - ExprKind::Call(path_expr, _) => { - if let ExprKind::Path(qpath) = &path_expr.kind { - typeck_results - .qpath_res(qpath, path_expr.hir_id) - .opt_def_id() - .map_or(true, |fun_id| !match_panic_def_id(cx, fun_id)) - } else { - true - } - }, _ => true, } } diff --git a/clippy_lints/src/bool_assert_comparison.rs b/clippy_lints/src/bool_assert_comparison.rs index d0b8c52a36a..c50e214be28 100644 --- a/clippy_lints/src/bool_assert_comparison.rs +++ b/clippy_lints/src/bool_assert_comparison.rs @@ -1,4 +1,5 @@ -use clippy_utils::{diagnostics::span_lint_and_sugg, higher, is_direct_expn_of, ty::implements_trait}; +use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; +use clippy_utils::{diagnostics::span_lint_and_sugg, ty::implements_trait}; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Lit}; @@ -41,7 +42,7 @@ fn is_bool_lit(e: &Expr<'_>) -> bool { ) && !e.span.from_expansion() } -fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { +fn is_impl_not_trait_with_bool_out(cx: &LateContext<'_>, e: &Expr<'_>) -> bool { let ty = cx.typeck_results().expr_ty(e); cx.tcx @@ -66,44 +67,40 @@ fn is_impl_not_trait_with_bool_out(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let macros = ["assert_eq", "debug_assert_eq"]; - let inverted_macros = ["assert_ne", "debug_assert_ne"]; - - for mac in macros.iter().chain(inverted_macros.iter()) { - if let Some(span) = is_direct_expn_of(expr.span, mac) { - if let Some(args) = higher::extract_assert_macro_args(expr) { - if let [a, b, ..] = args[..] { - let nb_bool_args = usize::from(is_bool_lit(a)) + usize::from(is_bool_lit(b)); - - if nb_bool_args != 1 { - // If there are two boolean arguments, we definitely don't understand - // what's going on, so better leave things as is... - // - // Or there is simply no boolean and then we can leave things as is! - return; - } - - if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) { - // At this point the expression which is not a boolean - // literal does not implement Not trait with a bool output, - // so we cannot suggest to rewrite our code - return; - } + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + let macro_name = cx.tcx.item_name(macro_call.def_id); + if !matches!( + macro_name.as_str(), + "assert_eq" | "debug_assert_eq" | "assert_ne" | "debug_assert_ne" + ) { + return; + } + let Some ((a, b, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; + if !(is_bool_lit(a) ^ is_bool_lit(b)) { + // If there are two boolean arguments, we definitely don't understand + // what's going on, so better leave things as is... + // + // Or there is simply no boolean and then we can leave things as is! + return; + } - let non_eq_mac = &mac[..mac.len() - 3]; - span_lint_and_sugg( - cx, - BOOL_ASSERT_COMPARISON, - span, - &format!("used `{}!` with a literal bool", mac), - "replace it with", - format!("{}!(..)", non_eq_mac), - Applicability::MaybeIncorrect, - ); - return; - } - } - } + if !is_impl_not_trait_with_bool_out(cx, a) || !is_impl_not_trait_with_bool_out(cx, b) { + // At this point the expression which is not a boolean + // literal does not implement Not trait with a bool output, + // so we cannot suggest to rewrite our code + return; } + + let macro_name = macro_name.as_str(); + let non_eq_mac = ¯o_name[..macro_name.len() - 3]; + span_lint_and_sugg( + cx, + BOOL_ASSERT_COMPARISON, + macro_call.span, + &format!("used `{}!` with a literal bool", macro_name), + "replace it with", + format!("{}!(..)", non_eq_mac), + Applicability::MaybeIncorrect, + ); } } diff --git a/clippy_lints/src/borrow_as_ptr.rs b/clippy_lints/src/borrow_as_ptr.rs new file mode 100644 index 00000000000..b8f5217af2b --- /dev/null +++ b/clippy_lints/src/borrow_as_ptr.rs @@ -0,0 +1,97 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_no_std_crate; +use clippy_utils::source::snippet_opt; +use clippy_utils::{meets_msrv, msrvs}; +use if_chain::if_chain; +use rustc_errors::Applicability; +use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for the usage of `&expr as *const T` or + /// `&mut expr as *mut T`, and suggest using `ptr::addr_of` or + /// `ptr::addr_of_mut` instead. + /// + /// ### Why is this bad? + /// This would improve readability and avoid creating a reference + /// that points to an uninitialized value or unaligned place. + /// Read the `ptr::addr_of` docs for more information. + /// + /// ### Example + /// ```rust + /// let val = 1; + /// let p = &val as *const i32; + /// + /// let mut val_mut = 1; + /// let p_mut = &mut val_mut as *mut i32; + /// ``` + /// Use instead: + /// ```rust + /// let val = 1; + /// let p = std::ptr::addr_of!(val); + /// + /// let mut val_mut = 1; + /// let p_mut = std::ptr::addr_of_mut!(val_mut); + /// ``` + #[clippy::version = "1.60.0"] + pub BORROW_AS_PTR, + pedantic, + "borrowing just to cast to a raw pointer" +} + +impl_lint_pass!(BorrowAsPtr => [BORROW_AS_PTR]); + +pub struct BorrowAsPtr { + msrv: Option<RustcVersion>, +} + +impl BorrowAsPtr { + #[must_use] + pub fn new(msrv: Option<RustcVersion>) -> Self { + Self { msrv } + } +} + +impl<'tcx> LateLintPass<'tcx> for BorrowAsPtr { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::BORROW_AS_PTR) { + return; + } + + if expr.span.from_expansion() { + return; + } + + if_chain! { + if let ExprKind::Cast(left_expr, ty) = &expr.kind; + if let TyKind::Ptr(_) = ty.kind; + if let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = &left_expr.kind; + + then { + let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" }; + let macro_name = match mutability { + Mutability::Not => "addr_of", + Mutability::Mut => "addr_of_mut", + }; + + span_lint_and_sugg( + cx, + BORROW_AS_PTR, + expr.span, + "borrow as raw pointer", + "try", + format!( + "{}::ptr::{}!({})", + core_or_std, + macro_name, + snippet_opt(cx, e.span).unwrap() + ), + Applicability::MachineApplicable, + ); + } + } + } +} diff --git a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs index 3f286dd9e2f..e8f39cd3709 100644 --- a/clippy_lints/src/case_sensitive_file_extension_comparisons.rs +++ b/clippy_lints/src/case_sensitive_file_extension_comparisons.rs @@ -67,7 +67,7 @@ fn check_case_sensitive_file_extension_comparison(ctx: &LateContext<'_>, expr: & None } -impl LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons { +impl<'tcx> LateLintPass<'tcx> for CaseSensitiveFileExtensionComparisons { fn check_expr(&mut self, ctx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if let Some(span) = check_case_sensitive_file_extension_comparison(ctx, expr) { span_lint_and_help( diff --git a/clippy_lints/src/casts/cast_ptr_alignment.rs b/clippy_lints/src/casts/cast_ptr_alignment.rs index 248b35b024e..b9de5510455 100644 --- a/clippy_lints/src/casts/cast_ptr_alignment.rs +++ b/clippy_lints/src/casts/cast_ptr_alignment.rs @@ -9,7 +9,7 @@ use rustc_span::symbol::sym; use super::CAST_PTR_ALIGNMENT; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if let ExprKind::Cast(cast_expr, cast_to) = expr.kind { if is_hir_ty_cfg_dependant(cx, cast_to) { return; diff --git a/clippy_lints/src/casts/cast_ref_to_mut.rs b/clippy_lints/src/casts/cast_ref_to_mut.rs index d9bf1ea58b9..15f2f81f407 100644 --- a/clippy_lints/src/casts/cast_ref_to_mut.rs +++ b/clippy_lints/src/casts/cast_ref_to_mut.rs @@ -6,7 +6,7 @@ use rustc_middle::ty; use super::CAST_REF_TO_MUT; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let ExprKind::Unary(UnOp::Deref, e) = &expr.kind; if let ExprKind::Cast(e, t) = &e.kind; diff --git a/clippy_lints/src/casts/char_lit_as_u8.rs b/clippy_lints/src/casts/char_lit_as_u8.rs index 099a0de881f..7cc406018db 100644 --- a/clippy_lints/src/casts/char_lit_as_u8.rs +++ b/clippy_lints/src/casts/char_lit_as_u8.rs @@ -9,7 +9,7 @@ use rustc_middle::ty::{self, UintTy}; use super::CHAR_LIT_AS_U8; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if let ExprKind::Cast(e, _) = &expr.kind; if let ExprKind::Lit(l) = &e.kind; diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index 3132d3a5cf0..fb04f93fbcf 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -12,7 +12,7 @@ use rustc_semver::RustcVersion; use super::PTR_AS_PTR; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: &Option<RustcVersion>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVersion>) { if !meets_msrv(msrv.as_ref(), &msrvs::POINTER_CAST) { return; } diff --git a/clippy_lints/src/copies.rs b/clippy_lints/src/copies.rs index d07bc23235b..73ce656ad15 100644 --- a/clippy_lints/src/copies.rs +++ b/clippy_lints/src/copies.rs @@ -316,7 +316,7 @@ struct BlockEqual { /// This function can also trigger the `IF_SAME_THEN_ELSE` in which case it'll return `None` to /// abort any further processing and avoid duplicate lint triggers. -fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<BlockEqual> { +fn scan_block_for_eq(cx: &LateContext<'_>, blocks: &[&Block<'_>]) -> Option<BlockEqual> { let mut start_eq = usize::MAX; let mut end_eq = usize::MAX; let mut expr_eq = true; @@ -385,11 +385,7 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option< }) } -fn check_for_warn_of_moved_symbol( - cx: &LateContext<'tcx>, - symbols: &FxHashSet<Symbol>, - if_expr: &'tcx Expr<'_>, -) -> bool { +fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &FxHashSet<Symbol>, if_expr: &Expr<'_>) -> bool { get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| { let ignore_span = block.span.shrink_to_lo().to(if_expr.span); @@ -419,13 +415,13 @@ fn check_for_warn_of_moved_symbol( } fn emit_branches_sharing_code_lint( - cx: &LateContext<'tcx>, + cx: &LateContext<'_>, start_stmts: usize, end_stmts: usize, lint_end: bool, warn_about_moved_symbol: bool, - blocks: &[&Block<'tcx>], - if_expr: &'tcx Expr<'_>, + blocks: &[&Block<'_>], + if_expr: &Expr<'_>, ) { if start_stmts == 0 && !lint_end { return; diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs index 6422f5aabe5..3070588483c 100644 --- a/clippy_lints/src/default.rs +++ b/clippy_lints/src/default.rs @@ -77,7 +77,7 @@ pub struct Default { impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]); -impl LateLintPass<'_> for Default { +impl<'tcx> LateLintPass<'tcx> for Default { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { if !expr.span.from_expansion(); @@ -110,7 +110,7 @@ impl LateLintPass<'_> for Default { } #[allow(clippy::too_many_lines)] - fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { // start from the `let mut _ = _::default();` and look at all the following // statements, see if they re-assign the fields of the binding let stmts_head = match block.stmts { diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs index 15215ac15cd..66b5f49817d 100644 --- a/clippy_lints/src/default_numeric_fallback.rs +++ b/clippy_lints/src/default_numeric_fallback.rs @@ -54,7 +54,7 @@ declare_clippy_lint! { declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]); -impl LateLintPass<'_> for DefaultNumericFallback { +impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback { fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { let mut visitor = NumericFallbackVisitor::new(cx); visitor.visit_body(body); diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index fa2b348591b..bf077a212fd 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -355,7 +355,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing { } } -fn try_parse_ref_op( +fn try_parse_ref_op<'tcx>( tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'_>, expr: &'tcx Expr<'_>, @@ -387,7 +387,7 @@ fn try_parse_ref_op( // Checks whether the type for a deref call actually changed the type, not just the mutability of // the reference. -fn deref_method_same_type(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { +fn deref_method_same_type(result_ty: Ty<'_>, arg_ty: Ty<'_>) -> bool { match (result_ty.kind(), arg_ty.kind()) { (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => TyS::same_type(result_ty, arg_ty), @@ -457,7 +457,7 @@ fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId, } /// Adjustments are sometimes made in the parent block rather than the expression itself. -fn find_adjustments( +fn find_adjustments<'tcx>( tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'_>, expr: &'tcx Expr<'_>, @@ -499,7 +499,7 @@ fn find_adjustments( } #[allow(clippy::needless_pass_by_value)] -fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) { +fn report(cx: &LateContext<'_>, expr: &Expr<'_>, state: State, data: StateData) { match state { State::DerefMethod { ty_changed_count, @@ -568,7 +568,7 @@ fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: Stat } impl Dereferencing { - fn check_local_usage(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, local: HirId) { + fn check_local_usage(&mut self, cx: &LateContext<'_>, e: &Expr<'_>, local: HirId) { if let Some(outer_pat) = self.ref_locals.get_mut(&local) { if let Some(pat) = outer_pat { // Check for auto-deref diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 6d4065907fb..73c00d97020 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -11,6 +11,9 @@ declare_clippy_lint! { /// ### What it does /// Denies the configured methods and functions in clippy.toml /// + /// Note: Even though this lint is warn-by-default, it will only trigger if + /// methods are defined in the clippy.toml file. + /// /// ### Why is this bad? /// Some methods are undesirable in certain contexts, and it's beneficial to /// lint for them as needed. @@ -49,14 +52,14 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.49.0"] pub DISALLOWED_METHODS, - nursery, + style, "use of a disallowed method call" } #[derive(Clone, Debug)] pub struct DisallowedMethods { conf_disallowed: Vec<conf::DisallowedMethod>, - disallowed: DefIdMap<Option<String>>, + disallowed: DefIdMap<usize>, } impl DisallowedMethods { @@ -72,17 +75,10 @@ impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { fn check_crate(&mut self, cx: &LateContext<'_>) { - for conf in &self.conf_disallowed { - let (path, reason) = match conf { - conf::DisallowedMethod::Simple(path) => (path, None), - conf::DisallowedMethod::WithReason { path, reason } => ( - path, - reason.as_ref().map(|reason| format!("{} (from clippy.toml)", reason)), - ), - }; - let segs: Vec<_> = path.split("::").collect(); + for (index, conf) in self.conf_disallowed.iter().enumerate() { + let segs: Vec<_> = conf.path().split("::").collect(); if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) { - self.disallowed.insert(id, reason); + self.disallowed.insert(id, index); } } } @@ -92,15 +88,17 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { Some(def_id) => def_id, None => return, }; - let reason = match self.disallowed.get(&def_id) { - Some(reason) => reason, + let conf = match self.disallowed.get(&def_id) { + Some(&index) => &self.conf_disallowed[index], None => return, }; - let func_path = cx.tcx.def_path_str(def_id); - let msg = format!("use of a disallowed method `{}`", func_path); + let msg = format!("use of a disallowed method `{}`", conf.path()); span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, &msg, |diag| { - if let Some(reason) = reason { - diag.note(reason); + if let conf::DisallowedMethod::WithReason { + reason: Some(reason), .. + } = conf + { + diag.note(&format!("{} (from clippy.toml)", reason)); } }); } diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index eaed4032713..ea4b49b46fe 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -14,6 +14,9 @@ declare_clippy_lint! { /// ### What it does /// Denies the configured types in clippy.toml. /// + /// Note: Even though this lint is warn-by-default, it will only trigger if + /// types are defined in the clippy.toml file. + /// /// ### Why is this bad? /// Some types are undesirable in certain contexts. /// @@ -44,7 +47,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.55.0"] pub DISALLOWED_TYPES, - nursery, + style, "use of disallowed types" } #[derive(Clone, Debug)] diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index 7c271773357..cb7d5ac7394 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -1,8 +1,9 @@ use clippy_utils::attrs::is_doc_hidden; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_note, span_lint_and_then}; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::source::{first_line_of_span, snippet_with_applicability}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty}; +use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty}; use if_chain::if_chain; use itertools::Itertools; use rustc_ast::ast::{Async, AttrKind, Attribute, Fn, FnRetTy, ItemKind}; @@ -13,7 +14,7 @@ use rustc_errors::emitter::EmitterWriter; use rustc_errors::{Applicability, Handler, SuggestionStyle}; use rustc_hir as hir; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; -use rustc_hir::{AnonConst, Expr, ExprKind, QPath}; +use rustc_hir::{AnonConst, Expr}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::map::Map; use rustc_middle::lint::in_external_macro; @@ -805,24 +806,17 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { return; } - // check for `begin_panic` - if_chain! { - if let ExprKind::Call(func_expr, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind; - if let Some(path_def_id) = path.res.opt_def_id(); - if match_panic_def_id(self.cx, path_def_id); - if is_expn_of(expr.span, "unreachable").is_none(); - if !is_expn_of_debug_assertions(expr.span); - then { - self.panic_span = Some(expr.span); + if let Some(macro_call) = root_macro_call_first_node(self.cx, expr) { + if is_panic(self.cx, macro_call.def_id) + || matches!( + self.cx.tcx.item_name(macro_call.def_id).as_str(), + "assert" | "assert_eq" | "assert_ne" | "todo" + ) + { + self.panic_span = Some(macro_call.span); } } - // check for `assert_eq` or `assert_ne` - if is_expn_of(expr.span, "assert_eq").is_some() || is_expn_of(expr.span, "assert_ne").is_some() { - self.panic_span = Some(expr.span); - } - // check for `unwrap` if let Some(arglists) = method_chain_args(expr, &["unwrap"]) { let receiver_ty = self.typeck_results.expr_ty(&arglists[0][0]).peel_refs(); @@ -844,8 +838,3 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> { NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) } } - -fn is_expn_of_debug_assertions(span: Span) -> bool { - const MACRO_NAMES: &[&str] = &["debug_assert", "debug_assert_eq", "debug_assert_ne"]; - MACRO_NAMES.iter().any(|name| is_expn_of(span, name).is_some()) -} diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 3d92eb16870..3ce239273e2 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -233,7 +233,7 @@ struct ContainsExpr<'tcx> { key: &'tcx Expr<'tcx>, call_ctxt: SyntaxContext, } -fn try_parse_contains(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> { +fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(MapType, ContainsExpr<'tcx>)> { let mut negated = false; let expr = peel_hir_expr_while(expr, |e| match e.kind { ExprKind::Unary(UnOp::Not, e) => { @@ -280,7 +280,7 @@ struct InsertExpr<'tcx> { key: &'tcx Expr<'tcx>, value: &'tcx Expr<'tcx>, } -fn try_parse_insert(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> { +fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> { if let ExprKind::MethodCall(_, _, [map, key, value], _) = expr.kind { let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?; if match_def_path(cx, id, &paths::BTREEMAP_INSERT) || match_def_path(cx, id, &paths::HASHMAP_INSERT) { @@ -301,7 +301,7 @@ enum Edit<'tcx> { /// An insertion into the map. Insertion(Insertion<'tcx>), } -impl Edit<'tcx> { +impl<'tcx> Edit<'tcx> { fn as_insertion(self) -> Option<Insertion<'tcx>> { if let Self::Insertion(i) = self { Some(i) } else { None } } @@ -532,7 +532,7 @@ struct InsertSearchResults<'tcx> { allow_insert_closure: bool, is_single_insert: bool, } -impl InsertSearchResults<'tcx> { +impl<'tcx> InsertSearchResults<'tcx> { fn as_single_insertion(&self) -> Option<Insertion<'tcx>> { self.is_single_insert.then(|| self.edits[0].as_insertion().unwrap()) } @@ -633,7 +633,7 @@ impl InsertSearchResults<'tcx> { } } -fn find_insert_calls( +fn find_insert_calls<'tcx>( cx: &LateContext<'tcx>, contains_expr: &ContainsExpr<'tcx>, expr: &'tcx Expr<'_>, diff --git a/clippy_lints/src/eq_op.rs b/clippy_lints/src/eq_op.rs index 10123460527..df75b815436 100644 --- a/clippy_lints/src/eq_op.rs +++ b/clippy_lints/src/eq_op.rs @@ -1,10 +1,11 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then}; +use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace}; use clippy_utils::source::snippet; use clippy_utils::ty::{implements_trait, is_copy}; -use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, is_expn_of, is_in_test_function}; +use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function}; use if_chain::if_chain; use rustc_errors::Applicability; -use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind}; +use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; @@ -68,32 +69,26 @@ declare_clippy_lint! { declare_lint_pass!(EqOp => [EQ_OP, OP_REF]); -const ASSERT_MACRO_NAMES: [&str; 4] = ["assert_eq", "assert_ne", "debug_assert_eq", "debug_assert_ne"]; - impl<'tcx> LateLintPass<'tcx> for EqOp { #[allow(clippy::similar_names, clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - if let ExprKind::Block(block, _) = e.kind { - for stmt in block.stmts { - for amn in &ASSERT_MACRO_NAMES { - if_chain! { - if is_expn_of(stmt.span, amn).is_some(); - if let StmtKind::Semi(matchexpr) = stmt.kind; - if let Some(macro_args) = higher::extract_assert_macro_args(matchexpr); - if macro_args.len() == 2; - let (lhs, rhs) = (macro_args[0], macro_args[1]); - if eq_expr_value(cx, lhs, rhs); - if !is_in_test_function(cx.tcx, e.hir_id); - then { - span_lint( - cx, - EQ_OP, - lhs.span.to(rhs.span), - &format!("identical args used in this `{}!` macro call", amn), - ); - } - } - } + if_chain! { + 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); + matches!(name.as_str(), "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne") + .then(|| (macro_call, name)) + }); + if let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn); + if eq_expr_value(cx, lhs, rhs); + if macro_call.is_local(); + if !is_in_test_function(cx.tcx, e.hir_id); + then { + span_lint( + cx, + EQ_OP, + lhs.span.to(rhs.span), + &format!("identical args used in this `{}!` macro call", macro_name), + ); } } if let ExprKind::Binary(op, left, right) = e.kind { diff --git a/clippy_lints/src/equatable_if_let.rs b/clippy_lints/src/equatable_if_let.rs index 06d128f5527..cf47e581ccb 100644 --- a/clippy_lints/src/equatable_if_let.rs +++ b/clippy_lints/src/equatable_if_let.rs @@ -56,7 +56,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool { } } -fn is_structural_partial_eq(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool { +fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> bool { if let Some(def_id) = cx.tcx.lang_items().eq_trait() { implements_trait(cx, ty, def_id, &[other.into()]) } else { diff --git a/clippy_lints/src/erasing_op.rs b/clippy_lints/src/erasing_op.rs index d49cec26be5..c1a84973c42 100644 --- a/clippy_lints/src/erasing_op.rs +++ b/clippy_lints/src/erasing_op.rs @@ -1,9 +1,11 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::span_lint; +use clippy_utils::ty::same_type_and_consts; + use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::TypeckResults; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::source_map::Span; declare_clippy_lint! { /// ### What it does @@ -35,24 +37,40 @@ impl<'tcx> LateLintPass<'tcx> for ErasingOp { return; } if let ExprKind::Binary(ref cmp, left, right) = e.kind { + let tck = cx.typeck_results(); match cmp.node { BinOpKind::Mul | BinOpKind::BitAnd => { - check(cx, left, e.span); - check(cx, right, e.span); + check(cx, tck, left, right, e); + check(cx, tck, right, left, e); }, - BinOpKind::Div => check(cx, left, e.span), + BinOpKind::Div => check(cx, tck, left, right, e), _ => (), } } } } -fn check(cx: &LateContext<'_>, e: &Expr<'_>, span: Span) { - if constant_simple(cx, cx.typeck_results(), e) == Some(Constant::Int(0)) { +fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool { + let input_ty = tck.expr_ty(input).peel_refs(); + let output_ty = tck.expr_ty(output).peel_refs(); + !same_type_and_consts(input_ty, output_ty) +} + +fn check<'tcx>( + cx: &LateContext<'tcx>, + tck: &TypeckResults<'tcx>, + op: &Expr<'tcx>, + other: &Expr<'tcx>, + parent: &Expr<'tcx>, +) { + if constant_simple(cx, tck, op) == Some(Constant::Int(0)) { + if different_types(tck, other, parent) { + return; + } span_lint( cx, ERASING_OP, - span, + parent.span, "this operation will always return zero. This is likely not the intended outcome", ); } diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index 5a4b4247104..b22515a3907 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher::VecArgs; use clippy_utils::source::snippet_opt; +use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::local_used_after_expr; use clippy_utils::{get_enclosing_loop_or_closure, higher, path_to_local, path_to_local_id}; use if_chain::if_chain; @@ -12,6 +13,7 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::subst::Subst; use rustc_middle::ty::{self, ClosureKind, Ty, TypeFoldable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::symbol::sym; declare_clippy_lint! { /// ### What it does @@ -113,6 +115,9 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { // A type param function ref like `T::f` is not 'static, however // it is if cast like `T::f as fn()`. This seems like a rustc bug. if !substs.types().any(|t| matches!(t.kind(), ty::Param(_))); + let callee_ty_unadjusted = cx.typeck_results().expr_ty(callee).peel_refs(); + if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Arc); + if !is_type_diagnostic_item(cx, callee_ty_unadjusted, sym::Rc); then { span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| { if let Some(mut snippet) = snippet_opt(cx, callee.span) { diff --git a/clippy_lints/src/explicit_write.rs b/clippy_lints/src/explicit_write.rs index 6b327b9ce17..98e5234e0aa 100644 --- a/clippy_lints/src/explicit_write.rs +++ b/clippy_lints/src/explicit_write.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; -use clippy_utils::higher::FormatArgsExpn; +use clippy_utils::macros::FormatArgsExpn; use clippy_utils::{is_expn_of, match_function_call, paths}; use if_chain::if_chain; use rustc_errors::Applicability; @@ -48,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { } else { None }; - if let Some(format_args) = FormatArgsExpn::parse(write_arg); + if let Some(format_args) = FormatArgsExpn::parse(cx, write_arg); then { let calling_macro = // ordering is important here, since `writeln!` uses `write!` internally @@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite { ) }; let msg = format!("use of `{}.unwrap()`", used); - if let [write_output] = *format_args.format_string_symbols { + if let [write_output] = *format_args.format_string_parts { let mut write_output = write_output.to_string(); if write_output.ends_with('\n') { write_output.pop(); diff --git a/clippy_lints/src/fallible_impl_from.rs b/clippy_lints/src/fallible_impl_from.rs index 05d300058cf..02f1baf27fa 100644 --- a/clippy_lints/src/fallible_impl_from.rs +++ b/clippy_lints/src/fallible_impl_from.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; +use clippy_utils::method_chain_args; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{is_expn_of, match_panic_def_id, method_chain_args}; use if_chain::if_chain; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -68,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[hir::ImplItemRef]) { use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; - use rustc_hir::{Expr, ExprKind, ImplItemKind, QPath}; + use rustc_hir::{Expr, ImplItemKind}; struct FindPanicUnwrap<'a, 'tcx> { lcx: &'a LateContext<'tcx>, @@ -80,14 +81,8 @@ fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_items: &[h type Map = Map<'tcx>; fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { - // check for `begin_panic` - if_chain! { - if let ExprKind::Call(func_expr, _) = expr.kind; - if let ExprKind::Path(QPath::Resolved(_, path)) = func_expr.kind; - if let Some(path_def_id) = path.res.opt_def_id(); - if match_panic_def_id(self.lcx, path_def_id); - if is_expn_of(expr.span, "unreachable").is_none(); - then { + if let Some(macro_call) = root_macro_call_first_node(self.lcx, expr) { + if is_panic(self.lcx, macro_call.def_id) { self.result.push(expr.span); } } diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs index 3f043e5f2f1..688d8f8630f 100644 --- a/clippy_lints/src/format.rs +++ b/clippy_lints/src/format.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::FormatExpn; +use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn}; use clippy_utils::source::{snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use if_chain::if_chain; @@ -43,38 +43,41 @@ declare_lint_pass!(UselessFormat => [USELESS_FORMAT]); impl<'tcx> LateLintPass<'tcx> for UselessFormat { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let FormatExpn { call_site, format_args } = match FormatExpn::parse(expr) { - Some(e) if !e.call_site.from_expansion() => e, - _ => return, + let (format_args, call_site) = if_chain! { + if let Some(macro_call) = root_macro_call_first_node(cx, expr); + if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id); + if let Some(format_args) = FormatArgsExpn::find_nested(cx, expr, macro_call.expn); + then { + (format_args, macro_call.span) + } else { + return + } }; let mut applicability = Applicability::MachineApplicable; if format_args.value_args.is_empty() { - if format_args.format_string_parts.is_empty() { - span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability); - } else { - if_chain! { - if let [e] = &*format_args.format_string_parts; - if let ExprKind::Lit(lit) = &e.kind; - if let Some(s_src) = snippet_opt(cx, lit.span); - then { + match *format_args.format_string_parts { + [] => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability), + [_] => { + if let Some(s_src) = snippet_opt(cx, format_args.format_string_span) { // Simulate macro expansion, converting {{ and }} to { and }. let s_expand = s_src.replace("{{", "{").replace("}}", "}"); let sugg = format!("{}.to_string()", s_expand); span_useless_format(cx, call_site, sugg, applicability); } - } + }, + [..] => {}, } } else if let [value] = *format_args.value_args { if_chain! { - if format_args.format_string_symbols == [kw::Empty]; + if format_args.format_string_parts == [kw::Empty]; if match cx.typeck_results().expr_ty(value).peel_refs().kind() { ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(sym::String, adt.did), ty::Str => true, _ => false, }; if let Some(args) = format_args.args(); - if args.iter().all(|arg| arg.is_display() && !arg.has_string_formatting()); + if args.iter().all(|arg| arg.format_trait == sym::Display && !arg.has_string_formatting()); then { let is_new_string = match value.kind { ExprKind::Binary(..) => true, diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index f0e1a67dcdd..ae423d799d7 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::higher::{FormatArgsArg, FormatArgsExpn, FormatExpn}; +use clippy_utils::macros::{FormatArgsArg, FormatArgsExpn}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::implements_trait; use clippy_utils::{is_diag_trait_item, match_def_path, paths}; @@ -83,7 +83,7 @@ const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[sym::format_macro, sym::std_panic_m impl<'tcx> LateLintPass<'tcx> for FormatArgs { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { - if let Some(format_args) = FormatArgsExpn::parse(expr); + if let Some(format_args) = FormatArgsExpn::parse(cx, expr); let expr_expn_data = expr.span.ctxt().outer_expn_data(); let outermost_expn_data = outermost_expn_data(expr_expn_data); if let Some(macro_def_id) = outermost_expn_data.macro_def_id; @@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { if let Some(args) = format_args.args(); then { for (i, arg) in args.iter().enumerate() { - if !arg.is_display() { + if arg.format_trait != sym::Display { continue; } if arg.has_string_formatting() { @@ -106,8 +106,8 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { if is_aliased(&args, i) { continue; } - check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg); - check_to_string_in_format_args(cx, name, arg); + check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.value); + check_to_string_in_format_args(cx, name, arg.value); } } } @@ -122,30 +122,31 @@ fn outermost_expn_data(expn_data: ExpnData) -> ExpnData { } } -fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &FormatArgsArg<'_>) { - if_chain! { - if FormatExpn::parse(arg.value).is_some(); - if !arg.value.span.ctxt().outer_expn_data().call_site.from_expansion(); - then { - span_lint_and_then( - cx, - FORMAT_IN_FORMAT_ARGS, - call_site, - &format!("`format!` in `{}!` args", name), - |diag| { - diag.help(&format!( - "combine the `format!(..)` arguments with the outer `{}!(..)` call", - name - )); - diag.help("or consider changing `format!` to `format_args!`"); - }, - ); - } +fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symbol, arg: &Expr<'_>) { + let expn_data = arg.span.ctxt().outer_expn_data(); + if expn_data.call_site.from_expansion() { + return; + } + let Some(mac_id) = expn_data.macro_def_id else { return }; + if !cx.tcx.is_diagnostic_item(sym::format_macro, mac_id) { + return; } + span_lint_and_then( + cx, + FORMAT_IN_FORMAT_ARGS, + call_site, + &format!("`format!` in `{}!` args", name), + |diag| { + diag.help(&format!( + "combine the `format!(..)` arguments with the outer `{}!(..)` call", + name + )); + diag.help("or consider changing `format!` to `format_args!`"); + }, + ); } -fn check_to_string_in_format_args<'tcx>(cx: &LateContext<'tcx>, name: Symbol, arg: &FormatArgsArg<'tcx>) { - let value = arg.value; +fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) { if_chain! { if !value.span.from_expansion(); if let ExprKind::MethodCall(_, _, [receiver], _) = value.kind; diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 866ff216f84..5ece2cc5ac4 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -53,7 +53,7 @@ impl FromOverInto { impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); -impl LateLintPass<'_> for FromOverInto { +impl<'tcx> LateLintPass<'tcx> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { if !meets_msrv(self.msrv.as_ref(), &msrvs::RE_REBALANCING_COHERENCE) { return; diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs index 73e800073b0..57b07513205 100644 --- a/clippy_lints/src/from_str_radix_10.rs +++ b/clippy_lints/src/from_str_radix_10.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]); -impl LateLintPass<'tcx> for FromStrRadix10 { +impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { if_chain! { if let ExprKind::Call(maybe_path, arguments) = &exp.kind; diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs index 77d08081c07..f2b4aefaead 100644 --- a/clippy_lints/src/functions/must_use.rs +++ b/clippy_lints/src/functions/must_use.rs @@ -18,7 +18,7 @@ use clippy_utils::{match_def_path, must_use_attr, return_ty, trait_ref_of_method use super::{DOUBLE_MUST_USE, MUST_USE_CANDIDATE, MUST_USE_UNIT}; -pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { +pub(super) fn check_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { let attrs = cx.tcx.hir().attrs(item.hir_id()); let attr = must_use_attr(attrs); if let hir::ItemKind::Fn(ref sig, ref _generics, ref body_id) = item.kind { @@ -40,7 +40,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { } } -pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { +pub(super) fn check_impl_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { if let hir::ImplItemKind::Fn(ref sig, ref body_id) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -62,7 +62,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem< } } -pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, ref eid) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); diff --git a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index f83789bb219..6d829a18b2e 100644 --- a/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -9,7 +9,7 @@ use clippy_utils::{iter_input_pats, path_to_local}; use super::NOT_UNSAFE_PTR_ARG_DEREF; -pub(super) fn check_fn( +pub(super) fn check_fn<'tcx>( cx: &LateContext<'tcx>, kind: intravisit::FnKind<'tcx>, decl: &'tcx hir::FnDecl<'tcx>, @@ -25,14 +25,14 @@ pub(super) fn check_fn( check_raw_ptr(cx, unsafety, decl, body, cx.tcx.hir().local_def_id(hir_id)); } -pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { +pub(super) fn check_trait_item<'tcx>(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(eid)) = item.kind { let body = cx.tcx.hir().body(eid); check_raw_ptr(cx, sig.header.unsafety, sig.decl, body, item.def_id); } } -fn check_raw_ptr( +fn check_raw_ptr<'tcx>( cx: &LateContext<'tcx>, unsafety: hir::Unsafety, decl: &'tcx hir::FnDecl<'tcx>, diff --git a/clippy_lints/src/functions/result_unit_err.rs b/clippy_lints/src/functions/result_unit_err.rs index 71f6f87ae60..73f08a04989 100644 --- a/clippy_lints/src/functions/result_unit_err.rs +++ b/clippy_lints/src/functions/result_unit_err.rs @@ -13,7 +13,7 @@ use clippy_utils::ty::is_type_diagnostic_item; use super::RESULT_UNIT_ERR; -pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { +pub(super) fn check_item(cx: &LateContext<'_>, item: &hir::Item<'_>) { if let hir::ItemKind::Fn(ref sig, ref _generics, _) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -23,7 +23,7 @@ pub(super) fn check_item(cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { } } -pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { +pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &hir::ImplItem<'_>) { if let hir::ImplItemKind::Fn(ref sig, _) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); @@ -33,7 +33,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem< } } -pub(super) fn check_trait_item(cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { +pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>) { if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { let is_public = cx.access_levels.is_exported(item.def_id); let fn_header_span = item.span.with_hi(sig.decl.output.span().hi()); diff --git a/clippy_lints/src/functions/too_many_arguments.rs b/clippy_lints/src/functions/too_many_arguments.rs index 63a14d8d4cd..3af960491ed 100644 --- a/clippy_lints/src/functions/too_many_arguments.rs +++ b/clippy_lints/src/functions/too_many_arguments.rs @@ -9,9 +9,9 @@ use clippy_utils::is_trait_impl_item; use super::TOO_MANY_ARGUMENTS; pub(super) fn check_fn( - cx: &LateContext<'tcx>, - kind: intravisit::FnKind<'tcx>, - decl: &'tcx hir::FnDecl<'_>, + cx: &LateContext<'_>, + kind: intravisit::FnKind<'_>, + decl: &hir::FnDecl<'_>, span: Span, hir_id: hir::HirId, too_many_arguments_threshold: u64, @@ -39,11 +39,7 @@ pub(super) fn check_fn( } } -pub(super) fn check_trait_item( - cx: &LateContext<'tcx>, - item: &'tcx hir::TraitItem<'_>, - too_many_arguments_threshold: u64, -) { +pub(super) fn check_trait_item(cx: &LateContext<'_>, item: &hir::TraitItem<'_>, too_many_arguments_threshold: u64) { if let hir::TraitItemKind::Fn(ref sig, _) = item.kind { // don't lint extern functions decls, it's not their fault if sig.header.abi == Abi::Rust { diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs index 65efbbab41a..54bdea7ea25 100644 --- a/clippy_lints/src/functions/too_many_lines.rs +++ b/clippy_lints/src/functions/too_many_lines.rs @@ -11,9 +11,9 @@ use super::TOO_MANY_LINES; pub(super) fn check_fn( cx: &LateContext<'_>, - kind: FnKind<'tcx>, + kind: FnKind<'_>, span: Span, - body: &'tcx hir::Body<'_>, + body: &hir::Body<'_>, too_many_lines_threshold: u64, ) { // Closures must be contained in a parent body, which will be checked for `too_many_lines`. diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 16e5c5ca603..9525c163ece 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -55,7 +55,7 @@ impl IfThenSomeElseNone { impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); -impl LateLintPass<'_> for IfThenSomeElseNone { +impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) { if !meets_msrv(self.msrv.as_ref(), &msrvs::BOOL_THEN) { return; diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs index 07caeada80d..d650d6e9a85 100644 --- a/clippy_lints/src/implicit_return.rs +++ b/clippy_lints/src/implicit_return.rs @@ -94,8 +94,8 @@ fn get_call_site(span: Span, ctxt: SyntaxContext) -> Option<Span> { } fn lint_implicit_returns( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'_>, + cx: &LateContext<'_>, + expr: &Expr<'_>, // The context of the function body. ctxt: SyntaxContext, // Whether the expression is from a macro expansion. diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 388bb3727f9..3d44a669d8f 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -63,7 +63,7 @@ declare_clippy_lint! { declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); -impl LateLintPass<'_> for InconsistentStructConstructor { +impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { if !expr.span.from_expansion(); diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 69f1c90beec..073313e2bad 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -69,7 +69,7 @@ impl IndexRefutableSlice { impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]); -impl LateLintPass<'_> for IndexRefutableSlice { +impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if_chain! { if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some(); diff --git a/clippy_lints/src/init_numbered_fields.rs b/clippy_lints/src/init_numbered_fields.rs index 5fe6725b581..9284e002409 100644 --- a/clippy_lints/src/init_numbered_fields.rs +++ b/clippy_lints/src/init_numbered_fields.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::in_macro; use clippy_utils::source::snippet_with_applicability; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -46,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { if let ExprKind::Struct(path, fields, None) = e.kind { if !fields.is_empty() - && !in_macro(e.span) + && !e.span.from_expansion() && fields .iter() .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit)) diff --git a/clippy_lints/src/iter_not_returning_iterator.rs b/clippy_lints/src/iter_not_returning_iterator.rs index 0af6b3b7d46..d3bdc819a9f 100644 --- a/clippy_lints/src/iter_not_returning_iterator.rs +++ b/clippy_lints/src/iter_not_returning_iterator.rs @@ -1,8 +1,7 @@ -use clippy_utils::{diagnostics::span_lint, return_ty, ty::implements_trait}; -use rustc_hir::{ImplItem, ImplItemKind}; +use clippy_utils::{diagnostics::span_lint, get_parent_node, ty::implements_trait}; +use rustc_hir::{def_id::LocalDefId, FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::symbol::kw; use rustc_span::symbol::sym; declare_clippy_lint! { @@ -40,26 +39,52 @@ declare_clippy_lint! { declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]); -impl LateLintPass<'_> for IterNotReturningIterator { - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) { - let name = impl_item.ident.name.as_str(); - if_chain! { - if let ImplItemKind::Fn(fn_sig, _) = &impl_item.kind; - let ret_ty = return_ty(cx, impl_item.hir_id()); - if matches!(name, "iter" | "iter_mut"); - if let [param] = cx.tcx.fn_arg_names(impl_item.def_id); - if param.name == kw::SelfLower; - if let Some(iter_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator); - if !implements_trait(cx, ret_ty, iter_trait_id, &[]); +impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator { + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + let name = item.ident.name.as_str(); + if matches!(name, "iter" | "iter_mut") { + if let TraitItemKind::Fn(fn_sig, _) = &item.kind { + check_sig(cx, name, fn_sig, item.def_id); + } + } + } - then { - span_lint( - cx, - ITER_NOT_RETURNING_ITERATOR, - fn_sig.span, - &format!("this method is named `{}` but its return type does not implement `Iterator`", name), - ); + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) { + let name = item.ident.name.as_str(); + if matches!(name, "iter" | "iter_mut") + && !matches!( + get_parent_node(cx.tcx, item.hir_id()), + Some(Node::Item(Item { kind: ItemKind::Impl(i), .. })) if i.of_trait.is_some() + ) + { + if let ImplItemKind::Fn(fn_sig, _) = &item.kind { + check_sig(cx, name, fn_sig, item.def_id); } } } } + +fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId) { + if sig.decl.implicit_self.has_implicit_self() { + let ret_ty = cx.tcx.fn_sig(fn_id).skip_binder().output(); + let ret_ty = cx + .tcx + .try_normalize_erasing_regions(cx.param_env, ret_ty) + .unwrap_or(ret_ty); + if cx + .tcx + .get_diagnostic_item(sym::Iterator) + .map_or(false, |iter_id| !implements_trait(cx, ret_ty, iter_id, &[])) + { + span_lint( + cx, + ITER_NOT_RETURNING_ITERATOR, + sig.span, + &format!( + "this method is named `{}` but its return type does not implement `Iterator`", + name + ), + ); + } + } +} diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs index 64f6d62fbdc..e1168c3f602 100644 --- a/clippy_lints/src/len_zero.rs +++ b/clippy_lints/src/len_zero.rs @@ -245,7 +245,7 @@ enum LenOutput<'tcx> { Option(DefId), Result(DefId, Ty<'tcx>), } -fn parse_len_output(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> { +fn parse_len_output<'tcx>(cx: &LateContext<'_>, sig: FnSig<'tcx>) -> Option<LenOutput<'tcx>> { match *sig.output().kind() { ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral), ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did) => { diff --git a/clippy_lints/src/lib.register_all.rs b/clippy_lints/src/lib.register_all.rs index 944411087e9..26fb4259952 100644 --- a/clippy_lints/src/lib.register_all.rs +++ b/clippy_lints/src/lib.register_all.rs @@ -37,6 +37,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(derivable_impls::DERIVABLE_IMPLS), LintId::of(derive::DERIVE_HASH_XOR_EQ), LintId::of(derive::DERIVE_ORD_XOR_PARTIAL_ORD), + LintId::of(disallowed_methods::DISALLOWED_METHODS), + LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(doc::MISSING_SAFETY_DOC), LintId::of(doc::NEEDLESS_DOCTEST_MAIN), LintId::of(double_comparison::DOUBLE_COMPARISONS), @@ -113,6 +115,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(loops::WHILE_LET_ON_ITERATOR), LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), + LintId::of(manual_bits::MANUAL_BITS), LintId::of(manual_map::MANUAL_MAP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(manual_strip::MANUAL_STRIP), @@ -204,7 +207,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![ LintId::of(mut_key::MUTABLE_KEY_TYPE), LintId::of(mut_mutex_lock::MUT_MUTEX_LOCK), LintId::of(mut_reference::UNNECESSARY_MUT_PASSED), - LintId::of(mutex_atomic::MUTEX_ATOMIC), LintId::of(needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE), LintId::of(needless_bool::BOOL_COMPARISON), LintId::of(needless_bool::NEEDLESS_BOOL), diff --git a/clippy_lints/src/lib.register_lints.rs b/clippy_lints/src/lib.register_lints.rs index 002122793f3..746bdb19c3d 100644 --- a/clippy_lints/src/lib.register_lints.rs +++ b/clippy_lints/src/lib.register_lints.rs @@ -3,33 +3,33 @@ // Manual edits will be overwritten. store.register_lints(&[ - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::CLIPPY_LINTS_INTERNAL, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::COMPILER_LINT_FUNCTIONS, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::DEFAULT_LINT, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::IF_CHAIN_STYLE, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::INTERNING_DEFINED_SYMBOL, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::INVALID_CLIPPY_VERSION_ATTRIBUTE, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::INVALID_PATHS, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::LINT_WITHOUT_LINT_PASS, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::MATCH_TYPE_ON_DIAGNOSTIC_ITEM, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::MISSING_CLIPPY_VERSION_ATTRIBUTE, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::OUTER_EXPN_EXPN_DATA, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::PRODUCE_ICE, - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] utils::internal_lints::UNNECESSARY_SYMBOL_STR, absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS, approx_const::APPROX_CONSTANT, @@ -59,6 +59,7 @@ store.register_lints(&[ bool_assert_comparison::BOOL_ASSERT_COMPARISON, booleans::LOGIC_BUG, booleans::NONMINIMAL_BOOL, + borrow_as_ptr::BORROW_AS_PTR, bytecount::NAIVE_BYTECOUNT, cargo_common_metadata::CARGO_COMMON_METADATA, case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, @@ -225,6 +226,7 @@ store.register_lints(&[ main_recursion::MAIN_RECURSION, manual_assert::MANUAL_ASSERT, manual_async_fn::MANUAL_ASYNC_FN, + manual_bits::MANUAL_BITS, manual_map::MANUAL_MAP, manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE, manual_ok_or::MANUAL_OK_OR, @@ -435,6 +437,7 @@ store.register_lints(&[ shadow::SHADOW_REUSE, shadow::SHADOW_SAME, shadow::SHADOW_UNRELATED, + single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES, single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS, size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT, slow_vector_initialization::SLOW_VECTOR_INITIALIZATION, diff --git a/clippy_lints/src/lib.register_nursery.rs b/clippy_lints/src/lib.register_nursery.rs index e3cf0670018..a7353790100 100644 --- a/clippy_lints/src/lib.register_nursery.rs +++ b/clippy_lints/src/lib.register_nursery.rs @@ -6,8 +6,6 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(attrs::EMPTY_LINE_AFTER_OUTER_ATTR), LintId::of(cognitive_complexity::COGNITIVE_COMPLEXITY), LintId::of(copies::BRANCHES_SHARING_CODE), - LintId::of(disallowed_methods::DISALLOWED_METHODS), - LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(equatable_if_let::EQUATABLE_IF_LET), LintId::of(fallible_impl_from::FALLIBLE_IMPL_FROM), LintId::of(floating_point_arithmetic::IMPRECISE_FLOPS), @@ -17,6 +15,7 @@ store.register_group(true, "clippy::nursery", Some("clippy_nursery"), vec![ LintId::of(let_if_seq::USELESS_LET_IF_SEQ), LintId::of(missing_const_for_fn::MISSING_CONST_FOR_FN), LintId::of(mutable_debug_assertion::DEBUG_ASSERT_WITH_MUT_CALL), + LintId::of(mutex_atomic::MUTEX_ATOMIC), LintId::of(mutex_atomic::MUTEX_INTEGER), LintId::of(non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY), LintId::of(nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES), diff --git a/clippy_lints/src/lib.register_pedantic.rs b/clippy_lints/src/lib.register_pedantic.rs index 70a4a624378..1744b7c8250 100644 --- a/clippy_lints/src/lib.register_pedantic.rs +++ b/clippy_lints/src/lib.register_pedantic.rs @@ -7,6 +7,7 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![ LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK), LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF), LintId::of(bit_mask::VERBOSE_BIT_MASK), + LintId::of(borrow_as_ptr::BORROW_AS_PTR), LintId::of(bytecount::NAIVE_BYTECOUNT), LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS), LintId::of(casts::CAST_LOSSLESS), diff --git a/clippy_lints/src/lib.register_perf.rs b/clippy_lints/src/lib.register_perf.rs index 2ea0b696f1f..f9ffd4cf829 100644 --- a/clippy_lints/src/lib.register_perf.rs +++ b/clippy_lints/src/lib.register_perf.rs @@ -19,7 +19,6 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![ LintId::of(methods::SINGLE_CHAR_PATTERN), LintId::of(methods::UNNECESSARY_TO_OWNED), LintId::of(misc::CMP_OWNED), - LintId::of(mutex_atomic::MUTEX_ATOMIC), LintId::of(redundant_clone::REDUNDANT_CLONE), LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION), LintId::of(stable_sort_primitive::STABLE_SORT_PRIMITIVE), diff --git a/clippy_lints/src/lib.register_restriction.rs b/clippy_lints/src/lib.register_restriction.rs index eab389a9bd8..e7e2798da7d 100644 --- a/clippy_lints/src/lib.register_restriction.rs +++ b/clippy_lints/src/lib.register_restriction.rs @@ -54,6 +54,7 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve LintId::of(shadow::SHADOW_REUSE), LintId::of(shadow::SHADOW_SAME), LintId::of(shadow::SHADOW_UNRELATED), + LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES), LintId::of(strings::STRING_ADD), LintId::of(strings::STRING_SLICE), LintId::of(strings::STRING_TO_STRING), diff --git a/clippy_lints/src/lib.register_style.rs b/clippy_lints/src/lib.register_style.rs index 1a0b869d40a..05211476ff2 100644 --- a/clippy_lints/src/lib.register_style.rs +++ b/clippy_lints/src/lib.register_style.rs @@ -16,6 +16,8 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(comparison_chain::COMPARISON_CHAIN), LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT), LintId::of(dereference::NEEDLESS_BORROW), + LintId::of(disallowed_methods::DISALLOWED_METHODS), + LintId::of(disallowed_types::DISALLOWED_TYPES), LintId::of(doc::MISSING_SAFETY_DOC), LintId::of(doc::NEEDLESS_DOCTEST_MAIN), LintId::of(enum_variants::ENUM_VARIANT_NAMES), @@ -41,6 +43,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![ LintId::of(loops::WHILE_LET_ON_ITERATOR), LintId::of(main_recursion::MAIN_RECURSION), LintId::of(manual_async_fn::MANUAL_ASYNC_FN), + LintId::of(manual_bits::MANUAL_BITS), LintId::of(manual_map::MANUAL_MAP), LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE), LintId::of(map_clone::MAP_CLONE), diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index d4687a1e287..79e9882fef4 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -4,7 +4,6 @@ #![feature(box_patterns)] #![feature(control_flow_enum)] #![feature(drain_filter)] -#![feature(in_band_lifetimes)] #![feature(iter_intersperse)] #![feature(let_else)] #![feature(once_cell)] @@ -153,12 +152,9 @@ macro_rules! declare_clippy_lint { }; } -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] mod deprecated_lints; -#[cfg_attr( - any(feature = "internal-lints", feature = "metadata-collector-lint"), - allow(clippy::missing_clippy_version_attribute) -)] +#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; // begin lints modules, do not remove this comment, it’s used in `update_lints` @@ -177,6 +173,7 @@ mod blacklisted_name; mod blocks_in_if_conditions; mod bool_assert_comparison; mod booleans; +mod borrow_as_ptr; mod bytecount; mod cargo_common_metadata; mod case_sensitive_file_extension_comparisons; @@ -264,6 +261,7 @@ mod macro_use; mod main_recursion; mod manual_assert; mod manual_async_fn; +mod manual_bits; mod manual_map; mod manual_non_exhaustive; mod manual_ok_or; @@ -351,6 +349,7 @@ mod self_named_constructors; mod semicolon_if_nothing_returned; mod serde_api; mod shadow; +mod single_char_lifetime_names; mod single_component_path_imports; mod size_of_in_element_count; mod slow_vector_initialization; @@ -471,7 +470,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: include!("lib.register_restriction.rs"); include!("lib.register_pedantic.rs"); - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] include!("lib.register_internal.rs"); include!("lib.register_all.rs"); @@ -483,7 +482,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: include!("lib.register_cargo.rs"); include!("lib.register_nursery.rs"); - #[cfg(feature = "metadata-collector-lint")] + #[cfg(feature = "internal")] { if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) { store.register_late_pass(|| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new())); @@ -492,7 +491,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: } // all the internal lints - #[cfg(feature = "internal-lints")] + #[cfg(feature = "internal")] { store.register_early_pass(|| Box::new(utils::internal_lints::ClippyLintsInternal)); store.register_early_pass(|| Box::new(utils::internal_lints::ProduceIce)); @@ -858,6 +857,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| Box::new(needless_late_init::NeedlessLateInit)); store.register_late_pass(|| Box::new(return_self_not_must_use::ReturnSelfNotMustUse)); store.register_late_pass(|| Box::new(init_numbered_fields::NumberedFields)); + store.register_early_pass(|| Box::new(single_char_lifetime_names::SingleCharLifetimeNames)); + store.register_late_pass(move || Box::new(borrow_as_ptr::BorrowAsPtr::new(msrv))); + store.register_late_pass(move || Box::new(manual_bits::ManualBits::new(msrv))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/loops/empty_loop.rs b/clippy_lints/src/loops/empty_loop.rs index dda09fecdf9..823cf0f4322 100644 --- a/clippy_lints/src/loops/empty_loop.rs +++ b/clippy_lints/src/loops/empty_loop.rs @@ -5,7 +5,7 @@ use clippy_utils::{is_in_panic_handler, is_no_std_crate}; use rustc_hir::{Block, Expr}; use rustc_lint::LateContext; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, loop_block: &Block<'_>) { if loop_block.stmts.is_empty() && loop_block.expr.is_none() && !is_in_panic_handler(cx, expr) { let msg = "empty `loop {}` wastes CPU cycles"; let help = if is_no_std_crate(cx) { diff --git a/clippy_lints/src/loops/explicit_into_iter_loop.rs b/clippy_lints/src/loops/explicit_into_iter_loop.rs index 1bab0d99b69..17246cc5426 100644 --- a/clippy_lints/src/loops/explicit_into_iter_loop.rs +++ b/clippy_lints/src/loops/explicit_into_iter_loop.rs @@ -8,7 +8,7 @@ use rustc_lint::LateContext; use rustc_middle::ty::TyS; use rustc_span::symbol::sym; -pub(super) fn check(cx: &LateContext<'_>, self_arg: &'hir Expr<'hir>, call_expr: &Expr<'_>) { +pub(super) fn check(cx: &LateContext<'_>, self_arg: &Expr<'_>, call_expr: &Expr<'_>) { let self_ty = cx.typeck_results().expr_ty(self_arg); let self_ty_adjusted = cx.typeck_results().expr_ty_adjusted(self_arg); if !(TyS::same_type(self_ty, self_ty_adjusted) && is_trait_method(cx, call_expr, sym::IntoIterator)) { diff --git a/clippy_lints/src/loops/manual_memcpy.rs b/clippy_lints/src/loops/manual_memcpy.rs index c62fa5e998b..48c4015e07b 100644 --- a/clippy_lints/src/loops/manual_memcpy.rs +++ b/clippy_lints/src/loops/manual_memcpy.rs @@ -2,7 +2,7 @@ use super::{IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY}; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet; use clippy_utils::sugg::Sugg; -use clippy_utils::ty::is_type_diagnostic_item; +use clippy_utils::ty::is_copy; use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg}; use if_chain::if_chain; use rustc_ast::ast; @@ -62,15 +62,15 @@ pub(super) fn check<'tcx>( if_chain! { if let ExprKind::Index(base_left, idx_left) = lhs.kind; if let ExprKind::Index(base_right, idx_right) = rhs.kind; - if is_slice_like(cx, cx.typeck_results().expr_ty(base_left)); - if is_slice_like(cx, cx.typeck_results().expr_ty(base_right)); + if let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left)); + if get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some(); if let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts); if let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts); // Source and destination must be different if path_to_local(base_left) != path_to_local(base_right); then { - Some((IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left }, + Some((ty, IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left }, IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right })) } else { None @@ -78,7 +78,7 @@ pub(super) fn check<'tcx>( } }) }) - .map(|o| o.map(|(dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, &dst, &src))) + .map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src))) .collect::<Option<Vec<_>>>() .filter(|v| !v.is_empty()) .map(|v| v.join("\n ")); @@ -105,6 +105,7 @@ fn build_manual_memcpy_suggestion<'tcx>( start: &Expr<'_>, end: &Expr<'_>, limits: ast::RangeLimits, + elem_ty: Ty<'tcx>, dst: &IndexExpr<'_>, src: &IndexExpr<'_>, ) -> String { @@ -187,9 +188,16 @@ fn build_manual_memcpy_suggestion<'tcx>( .into() }; + let method_str = if is_copy(cx, elem_ty) { + "copy_from_slice" + } else { + "clone_from_slice" + }; + format!( - "{}.clone_from_slice(&{}[{}..{}]);", + "{}.{}(&{}[{}..{}]);", dst, + method_str, src_base_str, src_offset.maybe_par(), src_limit.maybe_par() @@ -203,7 +211,7 @@ fn build_manual_memcpy_suggestion<'tcx>( #[derive(Clone)] struct MinifyingSugg<'a>(Sugg<'a>); -impl Display for MinifyingSugg<'a> { +impl<'a> Display for MinifyingSugg<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { self.0.fmt(f) } @@ -324,14 +332,13 @@ struct Start<'hir> { kind: StartKind<'hir>, } -fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'_>) -> bool { - let is_slice = match ty.kind() { - ty::Ref(_, subty, _) => is_slice_like(cx, subty), - ty::Slice(..) | ty::Array(..) => true, - _ => false, - }; - - is_slice || is_type_diagnostic_item(cx, ty, sym::Vec) || is_type_diagnostic_item(cx, ty, sym::VecDeque) +fn get_slice_like_element_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { + match ty.kind() { + ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Vec, adt.did) => Some(subs.type_at(0)), + ty::Ref(_, subty, _) => get_slice_like_element_ty(cx, subty), + ty::Slice(ty) | ty::Array(ty, _) => Some(ty), + _ => None, + } } fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { diff --git a/clippy_lints/src/loops/mut_range_bound.rs b/clippy_lints/src/loops/mut_range_bound.rs index aedf0844937..37a57d8feb1 100644 --- a/clippy_lints/src/loops/mut_range_bound.rs +++ b/clippy_lints/src/loops/mut_range_bound.rs @@ -147,7 +147,7 @@ impl BreakAfterExprVisitor { } } -impl intravisit::Visitor<'tcx> for BreakAfterExprVisitor { +impl<'tcx> intravisit::Visitor<'tcx> for BreakAfterExprVisitor { type Map = Map<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { diff --git a/clippy_lints/src/loops/needless_collect.rs b/clippy_lints/src/loops/needless_collect.rs index ba895f35faa..6248680aa62 100644 --- a/clippy_lints/src/loops/needless_collect.rs +++ b/clippy_lints/src/loops/needless_collect.rs @@ -339,8 +339,8 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>( } } -fn get_captured_ids(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>) -> HirIdSet { - fn get_captured_ids_recursive(cx: &LateContext<'tcx>, ty: &'_ TyS<'_>, set: &mut HirIdSet) { +fn get_captured_ids(cx: &LateContext<'_>, ty: &'_ TyS<'_>) -> HirIdSet { + fn get_captured_ids_recursive(cx: &LateContext<'_>, ty: &'_ TyS<'_>, set: &mut HirIdSet) { match ty.kind() { ty::Adt(_, generics) => { for generic in *generics { diff --git a/clippy_lints/src/loops/never_loop.rs b/clippy_lints/src/loops/never_loop.rs index a3aa6be6afd..bb1b3e2a1ec 100644 --- a/clippy_lints/src/loops/never_loop.rs +++ b/clippy_lints/src/loops/never_loop.rs @@ -10,8 +10,8 @@ use rustc_span::Span; use std::iter::{once, Iterator}; pub(super) fn check( - cx: &LateContext<'tcx>, - block: &'tcx Block<'_>, + cx: &LateContext<'_>, + block: &Block<'_>, loop_id: HirId, span: Span, for_loop: Option<&ForLoop<'_>>, diff --git a/clippy_lints/src/loops/while_let_loop.rs b/clippy_lints/src/loops/while_let_loop.rs index 4dcd5c87722..8f57df0be6b 100644 --- a/clippy_lints/src/loops/while_let_loop.rs +++ b/clippy_lints/src/loops/while_let_loop.rs @@ -7,7 +7,7 @@ use rustc_hir::{Block, Expr, ExprKind, MatchSource, Pat, StmtKind}; use rustc_lint::{LateContext, LintContext}; use rustc_middle::lint::in_external_macro; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) { // extract the expression from the first statement (if any) in a block let inner_stmt_expr = extract_expr_from_first_stmt(loop_block); // or extract the first expression (if any) from the block diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs index b390476a664..750328d1d01 100644 --- a/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/clippy_lints/src/loops/while_let_on_iterator.rs @@ -8,12 +8,13 @@ use clippy_utils::{ use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor}; -use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, PatKind, QPath, UnOp}; +use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp}; use rustc_lint::LateContext; -use rustc_span::{symbol::sym, Span, Symbol}; +use rustc_middle::ty::adjustment::Adjust; +use rustc_span::{symbol::sym, Symbol}; -pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! { if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr); // check for `Some(..)` pattern if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind; @@ -27,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // get the loop containing the match expression if !uses_iter(cx, &iter_expr_struct, if_then); then { - (let_expr, iter_expr_struct, some_pat, expr) + (let_expr, iter_expr_struct, iter_expr, some_pat, expr) } else { return; } @@ -47,7 +48,11 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { // If the iterator is a field or the iterator is accessed after the loop is complete it needs to be // borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used // afterwards a mutable borrow of a field isn't necessary. - let by_ref = if !iter_expr.fields.is_empty() || needs_mutable_borrow(cx, &iter_expr, loop_expr) { + let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut) + || !iter_expr_struct.can_move + || !iter_expr_struct.fields.is_empty() + || needs_mutable_borrow(cx, &iter_expr_struct, loop_expr) + { ".by_ref()" } else { "" @@ -67,26 +72,36 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { #[derive(Debug)] struct IterExpr { - /// The span of the whole expression, not just the path and fields stored here. - span: Span, /// The fields used, in order of child to parent. fields: Vec<Symbol>, /// The path being used. path: Res, + /// Whether or not the iterator can be moved. + can_move: bool, } /// Parses any expression to find out which field of which variable is used. Will return `None` if /// the expression might have side effects. fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> { - let span = e.span; let mut fields = Vec::new(); + let mut can_move = true; loop { + if cx + .typeck_results() + .expr_adjustments(e) + .iter() + .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + { + // Custom deref impls need to borrow the whole value as it's captured by reference + can_move = false; + fields.clear(); + } match e.kind { ExprKind::Path(ref path) => { break Some(IterExpr { - span, fields, path: cx.qpath_res(path, e.hir_id), + can_move, }); }, ExprKind::Field(base, name) => { @@ -99,10 +114,12 @@ fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExp // Shouldn't have side effects, but there's no way to trace which field is used. So forget which fields have // already been seen. ExprKind::Index(base, idx) if !idx.can_have_side_effects() => { + can_move = false; fields.clear(); e = base; }, ExprKind::Unary(UnOp::Deref, base) => { + can_move = false; fields.clear(); e = base; }, @@ -174,7 +191,7 @@ fn is_expr_same_child_or_parent_field(cx: &LateContext<'_>, expr: &Expr<'_>, fie /// Strips off all field and path expressions. This will return true if a field or path has been /// skipped. Used to skip them after failing to check for equality. -fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) { +fn skip_fields_and_path<'tcx>(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool) { let mut e = expr; let e = loop { match e.kind { @@ -187,13 +204,13 @@ fn skip_fields_and_path(expr: &'tcx Expr<'_>) -> (Option<&'tcx Expr<'tcx>>, bool } /// Checks if the given expression uses the iterator. -fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool { +fn uses_iter<'tcx>(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr<'_>) -> bool { struct V<'a, 'b, 'tcx> { cx: &'a LateContext<'tcx>, iter_expr: &'b IterExpr, uses_iter: bool, } - impl Visitor<'tcx> for V<'_, '_, 'tcx> { + impl<'tcx> Visitor<'tcx> for V<'_, '_, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { NestedVisitorMap::None @@ -228,7 +245,7 @@ fn uses_iter(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tcx Expr } #[allow(clippy::too_many_lines)] -fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: &'tcx Expr<'_>) -> bool { +fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &Expr<'_>) -> bool { struct AfterLoopVisitor<'a, 'b, 'tcx> { cx: &'a LateContext<'tcx>, iter_expr: &'b IterExpr, @@ -236,7 +253,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: after_loop: bool, used_iter: bool, } - impl Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> { + impl<'tcx> Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { NestedVisitorMap::None @@ -275,7 +292,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr: found_local: bool, used_after: bool, } - impl Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> { + impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> { type Map = ErasedMap<'tcx>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { diff --git a/clippy_lints/src/macro_use.rs b/clippy_lints/src/macro_use.rs index 41f5a913b31..ca1ccb93bf3 100644 --- a/clippy_lints/src/macro_use.rs +++ b/clippy_lints/src/macro_use.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::in_macro; use clippy_utils::source::snippet; use hir::def::{DefKind, Res}; use if_chain::if_chain; @@ -104,34 +103,34 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports { } } } else { - if in_macro(item.span) { + if item.span.from_expansion() { self.push_unique_macro_pat_ty(cx, item.span); } } } } fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) { - if in_macro(attr.span) { + if attr.span.from_expansion() { self.push_unique_macro(cx, attr.span); } } fn check_expr(&mut self, cx: &LateContext<'_>, expr: &hir::Expr<'_>) { - if in_macro(expr.span) { + if expr.span.from_expansion() { self.push_unique_macro(cx, expr.span); } } fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) { - if in_macro(stmt.span) { + if stmt.span.from_expansion() { self.push_unique_macro(cx, stmt.span); } } fn check_pat(&mut self, cx: &LateContext<'_>, pat: &hir::Pat<'_>) { - if in_macro(pat.span) { + if pat.span.from_expansion() { self.push_unique_macro_pat_ty(cx, pat.span); } } fn check_ty(&mut self, cx: &LateContext<'_>, ty: &hir::Ty<'_>) { - if in_macro(ty.span) { + if ty.span.from_expansion() { self.push_unique_macro_pat_ty(cx, ty.span); } } diff --git a/clippy_lints/src/manual_assert.rs b/clippy_lints/src/manual_assert.rs index 5a2a965716c..26b53ab5d68 100644 --- a/clippy_lints/src/manual_assert.rs +++ b/clippy_lints/src/manual_assert.rs @@ -1,11 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::PanicExpn; +use clippy_utils::macros::{root_macro_call, FormatArgsExpn}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{is_expn_of, sugg}; +use clippy_utils::{peel_blocks_with_stmt, sugg}; use rustc_errors::Applicability; -use rustc_hir::{Block, Expr, ExprKind, StmtKind, UnOp}; +use rustc_hir::{Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::sym; declare_clippy_lint! { /// ### What it does @@ -34,65 +35,34 @@ declare_clippy_lint! { declare_lint_pass!(ManualAssert => [MANUAL_ASSERT]); -impl LateLintPass<'_> for ManualAssert { - fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { +impl<'tcx> LateLintPass<'tcx> for ManualAssert { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { if_chain! { - if let Expr { - kind: ExprKind:: If(cond, Expr { - kind: ExprKind::Block( - Block { - stmts: [stmt], - .. - }, - _), - .. - }, None), - .. - } = &expr; - if is_expn_of(stmt.span, "panic").is_some(); + if let ExprKind::If(cond, then, None) = expr.kind; if !matches!(cond.kind, ExprKind::Let(_)); - if let StmtKind::Semi(semi) = stmt.kind; + if !expr.span.from_expansion(); + let then = peel_blocks_with_stmt(then); + if let Some(macro_call) = root_macro_call(then.span); + if cx.tcx.item_name(macro_call.def_id) == sym::panic; if !cx.tcx.sess.source_map().is_multiline(cond.span); - + if let Some(format_args) = FormatArgsExpn::find_nested(cx, then, macro_call.expn); then { - let call = if_chain! { - if let ExprKind::Block(block, _) = semi.kind; - if let Some(init) = block.expr; - then { - init - } else { - semi - } - }; - let span = if let Some(panic_expn) = PanicExpn::parse(call) { - match *panic_expn.format_args.value_args { - [] => panic_expn.format_args.format_string_span, - [.., last] => panic_expn.format_args.format_string_span.to(last.span), - } - } else if let ExprKind::Call(_, [format_args]) = call.kind { - format_args.span - } else { - return - }; let mut applicability = Applicability::MachineApplicable; - let sugg = snippet_with_applicability(cx, span, "..", &mut applicability); - let cond_sugg = if let ExprKind::DropTemps(e, ..) = cond.kind { - if let Expr{kind: ExprKind::Unary(UnOp::Not, not_expr), ..} = e { - sugg::Sugg::hir_with_applicability(cx, not_expr, "..", &mut applicability).maybe_par().to_string() - } else { - format!("!{}", sugg::Sugg::hir_with_applicability(cx, e, "..", &mut applicability).maybe_par()) - } - } else { - format!("!{}", sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par()) + let format_args_snip = snippet_with_applicability(cx, format_args.inputs_span(), "..", &mut applicability); + let cond = cond.peel_drop_temps(); + let (cond, not) = match cond.kind { + ExprKind::Unary(UnOp::Not, e) => (e, ""), + _ => (cond, "!"), }; - + let cond_sugg = sugg::Sugg::hir_with_applicability(cx, cond, "..", &mut applicability).maybe_par(); + let sugg = format!("assert!({not}{cond_sugg}, {format_args_snip});"); span_lint_and_sugg( cx, MANUAL_ASSERT, expr.span, "only a `panic!` in `if`-then statement", "try", - format!("assert!({}, {});", cond_sugg, sugg), + sugg, Applicability::MachineApplicable, ); } diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs new file mode 100644 index 00000000000..50bf2527e39 --- /dev/null +++ b/clippy_lints/src/manual_bits.rs @@ -0,0 +1,107 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet_opt; +use clippy_utils::{match_def_path, meets_msrv, msrvs, paths}; +use rustc_ast::ast::LitKind; +use rustc_errors::Applicability; +use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::{self, Ty}; +use rustc_semver::RustcVersion; +use rustc_session::{declare_tool_lint, impl_lint_pass}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for uses of `std::mem::size_of::<T>() * 8` when + /// `T::BITS` is available. + /// + /// ### Why is this bad? + /// Can be written as the shorter `T::BITS`. + /// + /// ### Example + /// ```rust + /// std::mem::size_of::<usize>() * 8; + /// ``` + /// Use instead: + /// ```rust + /// usize::BITS; + /// ``` + #[clippy::version = "1.60.0"] + pub MANUAL_BITS, + style, + "manual implementation of `size_of::<T>() * 8` can be simplified with `T::BITS`" +} + +#[derive(Clone)] +pub struct ManualBits { + msrv: Option<RustcVersion>, +} + +impl ManualBits { + #[must_use] + pub fn new(msrv: Option<RustcVersion>) -> Self { + Self { msrv } + } +} + +impl_lint_pass!(ManualBits => [MANUAL_BITS]); + +impl<'tcx> LateLintPass<'tcx> for ManualBits { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { + if !meets_msrv(self.msrv.as_ref(), &msrvs::MANUAL_BITS) { + return; + } + + if_chain! { + if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind; + if let BinOpKind::Mul = &bin_op.node; + if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr); + if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)); + if let ExprKind::Lit(lit) = &other_expr.kind; + if let LitKind::Int(8, _) = lit.node; + + then { + span_lint_and_sugg( + cx, + MANUAL_BITS, + expr.span, + "usage of `mem::size_of::<T>()` to obtain the size of `T` in bits", + "consider using", + format!("{}::BITS", snippet_opt(cx, real_ty.span).unwrap()), + Applicability::MachineApplicable, + ); + } + } + } +} + +fn get_one_size_of_ty<'tcx>( + cx: &LateContext<'tcx>, + expr1: &'tcx Expr<'_>, + expr2: &'tcx Expr<'_>, +) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>, &'tcx Expr<'tcx>)> { + match (get_size_of_ty(cx, expr1), get_size_of_ty(cx, expr2)) { + (Some((real_ty, resolved_ty)), None) => Some((real_ty, resolved_ty, expr2)), + (None, Some((real_ty, resolved_ty))) => Some((real_ty, resolved_ty, expr1)), + _ => None, + } +} + +fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> { + if_chain! { + if let ExprKind::Call(count_func, _func_args) = expr.kind; + if let ExprKind::Path(ref count_func_qpath) = count_func.kind; + + if let QPath::Resolved(_, count_func_path) = count_func_qpath; + if let Some(segment_zero) = count_func_path.segments.get(0); + if let Some(args) = segment_zero.args; + if let Some(GenericArg::Type(real_ty)) = args.args.get(0); + + if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::MEM_SIZE_OF); + then { + cx.typeck_results().node_substs(count_func.hir_id).types().next().map(|resolved_ty| (real_ty, resolved_ty)) + } else { + None + } + } +} diff --git a/clippy_lints/src/manual_map.rs b/clippy_lints/src/manual_map.rs index 34a70ca76c6..8475e367b09 100644 --- a/clippy_lints/src/manual_map.rs +++ b/clippy_lints/src/manual_map.rs @@ -45,7 +45,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualMap => [MANUAL_MAP]); -impl LateLintPass<'_> for ManualMap { +impl<'tcx> LateLintPass<'tcx> for ManualMap { #[allow(clippy::too_many_lines)] fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let (scrutinee, then_pat, then_body, else_pat, else_body) = match IfLetOrMatch::parse(cx, expr) { @@ -219,7 +219,7 @@ impl LateLintPass<'_> for ManualMap { // Checks whether the expression could be passed as a function, or whether a closure is needed. // Returns the function to be passed to `map` if it exists. -fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { +fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { match expr.kind { ExprKind::Call(func, [arg]) if path_to_local_id(arg, binding) @@ -251,8 +251,13 @@ struct SomeExpr<'tcx> { // Try to parse into a recognized `Option` pattern. // i.e. `_`, `None`, `Some(..)`, or a reference to any of those. -fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> { - fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> { +fn try_parse_pattern<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> { + fn f<'tcx>( + cx: &LateContext<'tcx>, + pat: &'tcx Pat<'_>, + ref_count: usize, + ctxt: SyntaxContext, + ) -> Option<OptionPat<'tcx>> { match pat.kind { PatKind::Wild => Some(OptionPat::Wild), PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt), @@ -269,7 +274,7 @@ fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxCon } // Checks for an expression wrapped by the `Some` constructor. Returns the contained expression. -fn get_some_expr( +fn get_some_expr<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, needs_unsafe_block: bool, @@ -306,6 +311,6 @@ fn get_some_expr( } // Checks for the `None` value. -fn is_none_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { +fn is_none_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { matches!(peel_blocks(expr).kind, ExprKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone)) } diff --git a/clippy_lints/src/manual_ok_or.rs b/clippy_lints/src/manual_ok_or.rs index b60e2dc366b..bd083e3e9e2 100644 --- a/clippy_lints/src/manual_ok_or.rs +++ b/clippy_lints/src/manual_ok_or.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualOkOr => [MANUAL_OK_OR]); -impl LateLintPass<'_> for ManualOkOr { +impl<'tcx> LateLintPass<'tcx> for ManualOkOr { fn check_expr(&mut self, cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'tcx>) { if in_external_macro(cx.sess(), scrutinee.span) { return; diff --git a/clippy_lints/src/manual_unwrap_or.rs b/clippy_lints/src/manual_unwrap_or.rs index aac3c6e0de2..b3a91d9f18f 100644 --- a/clippy_lints/src/manual_unwrap_or.rs +++ b/clippy_lints/src/manual_unwrap_or.rs @@ -43,7 +43,7 @@ declare_clippy_lint! { declare_lint_pass!(ManualUnwrapOr => [MANUAL_UNWRAP_OR]); -impl LateLintPass<'_> for ManualUnwrapOr { +impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOr { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) { return; diff --git a/clippy_lints/src/match_str_case_mismatch.rs b/clippy_lints/src/match_str_case_mismatch.rs index 2c0fc218ca0..1fc7eb72142 100644 --- a/clippy_lints/src/match_str_case_mismatch.rs +++ b/clippy_lints/src/match_str_case_mismatch.rs @@ -55,7 +55,7 @@ enum CaseMethod { AsciiUppercase, } -impl LateLintPass<'_> for MatchStrCaseMismatch { +impl<'tcx> LateLintPass<'tcx> for MatchStrCaseMismatch { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { if !in_external_macro(cx.tcx.sess, expr.span); diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs index 5fa8f249e70..60dd957db01 100644 --- a/clippy_lints/src/matches.rs +++ b/clippy_lints/src/matches.rs @@ -2,16 +2,17 @@ use clippy_utils::consts::{constant, constant_full_int, miri_to_const, FullInt}; use clippy_utils::diagnostics::{ multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then, }; -use clippy_utils::higher; +use clippy_utils::macros::{is_panic, root_macro_call}; use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs}; use clippy_utils::visitors::is_local_used; use clippy_utils::{ - get_parent_expr, is_expn_of, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs, + get_parent_expr, is_lang_ctor, is_lint_allowed, is_refutable, is_unit_expr, is_wild, meets_msrv, msrvs, path_to_local, path_to_local_id, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, strip_pat_refs, }; +use clippy_utils::{higher, peel_blocks_with_stmt}; use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash}; use core::iter::{once, ExactSizeIterator}; use if_chain::if_chain; @@ -974,7 +975,8 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm } if_chain! { if matching_wild; - if is_panic_call(arm.body); + if let Some(macro_call) = root_macro_call(peel_blocks_with_stmt(arm.body).span); + if is_panic(cx, macro_call.def_id); then { // `Err(_)` or `Err(_e)` arm with `panic!` found span_lint_and_note(cx, @@ -997,7 +999,7 @@ enum CommonPrefixSearcher<'a> { Path(&'a [PathSegment<'a>]), Mixed, } -impl CommonPrefixSearcher<'a> { +impl<'a> CommonPrefixSearcher<'a> { fn with_path(&mut self, path: &'a [PathSegment<'a>]) { match path { [path @ .., _] => self.with_prefix(path), @@ -1179,22 +1181,6 @@ fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) }; } -// If the block contains only a `panic!` macro (as expression or statement) -fn is_panic_call(expr: &Expr<'_>) -> bool { - // Unwrap any wrapping blocks - let span = if let ExprKind::Block(block, _) = expr.kind { - match (&block.expr, block.stmts.len(), block.stmts.first()) { - (&Some(exp), 0, _) => exp.span, - (&None, 1, Some(stmt)) => stmt.span, - _ => return false, - } - } else { - expr.span - }; - - is_expn_of(span, "panic").is_some() && is_expn_of(span, "unreachable").is_none() -} - fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>) where 'b: 'a, @@ -1818,11 +1804,15 @@ mod redundant_pattern_match { /// Checks if the drop order for a type matters. Some std types implement drop solely to /// deallocate memory. For these types, and composites containing them, changing the drop order /// won't result in any observable side effects. - fn type_needs_ordered_drop(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + fn type_needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { type_needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default()) } - fn type_needs_ordered_drop_inner(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool { + fn type_needs_ordered_drop_inner<'tcx>( + cx: &LateContext<'tcx>, + ty: Ty<'tcx>, + seen: &mut FxHashSet<Ty<'tcx>>, + ) -> bool { if !seen.insert(ty) { return false; } @@ -1884,7 +1874,7 @@ mod redundant_pattern_match { // Checks if there are any temporaries created in the given expression for which drop order // matters. - fn temporaries_need_ordered_drop(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { + fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { struct V<'a, 'tcx> { cx: &'a LateContext<'tcx>, res: bool, diff --git a/clippy_lints/src/methods/expect_fun_call.rs b/clippy_lints/src/methods/expect_fun_call.rs index 0ec9387f9c4..0f39470f342 100644 --- a/clippy_lints/src/methods/expect_fun_call.rs +++ b/clippy_lints/src/methods/expect_fun_call.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::higher::FormatExpn; +use clippy_utils::macros::{root_macro_call_first_node, FormatArgsExpn}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; @@ -14,7 +14,13 @@ use super::EXPECT_FUN_CALL; /// Checks for the `EXPECT_FUN_CALL` lint. #[allow(clippy::too_many_lines)] -pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Span, name: &str, args: &[hir::Expr<'_>]) { +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + expr: &hir::Expr<'_>, + method_span: Span, + name: &str, + args: &'tcx [hir::Expr<'tcx>], +) { // Strip `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or // `&str` fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> { @@ -128,11 +134,12 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_span: Spa let mut applicability = Applicability::MachineApplicable; //Special handling for `format!` as arg_root - if let Some(format_expn) = FormatExpn::parse(arg_root) { - let span = match *format_expn.format_args.value_args { - [] => format_expn.format_args.format_string_span, - [.., last] => format_expn.format_args.format_string_span.to(last.span), - }; + if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) { + if !cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id) { + return; + } + let Some(format_args) = FormatArgsExpn::find_nested(cx, arg_root, macro_call.expn) else { return }; + let span = format_args.inputs_span(); let sugg = snippet_with_applicability(cx, span, "..", &mut applicability); span_lint_and_sugg( cx, diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs index 8ea9312c0f7..6436e28a63c 100644 --- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs +++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs @@ -37,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Exp } } -fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'tcx>) -> String { +fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) -> String { fn strip_angle_brackets(s: &str) -> Option<&str> { s.strip_prefix('<')?.strip_suffix('>') } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 4e33b2ff14c..1041f644e32 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -80,7 +80,6 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty, TyS}; use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; -use rustc_span::symbol::Symbol; use rustc_span::{sym, Span}; use rustc_typeck::hir_ty_to_ty; @@ -1895,6 +1894,11 @@ declare_clippy_lint! { /// ### Why is this bad? /// The unnecessary calls result in useless allocations. /// + /// ### Known problems + /// `unnecessary_to_owned` can falsely trigger if `IntoIterator::into_iter` is applied to an + /// owned copy of a resource and the resource is later used mutably. See + /// [#8148](https://github.com/rust-lang/rust-clippy/issues/8148). + /// /// ### Example /// ```rust /// let path = std::path::Path::new("x"); @@ -1997,24 +2001,16 @@ impl_lint_pass!(Methods => [ ]); /// Extracts a method call name, args, and `Span` of the method name. -fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(Symbol, &'tcx [hir::Expr<'tcx>], Span)> { +fn method_call<'tcx>(recv: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx str, &'tcx [hir::Expr<'tcx>], Span)> { if let ExprKind::MethodCall(path, span, args, _) = recv.kind { if !args.iter().any(|e| e.span.from_expansion()) { - return Some((path.ident.name, args, span)); + let name = path.ident.name.as_str(); + return Some((name, args, span)); } } None } -/// Same as `method_call` but the `Symbol` is dereferenced into a temporary `&str` -macro_rules! method_call { - ($expr:expr) => { - method_call($expr) - .as_ref() - .map(|&(ref name, args, span)| (name.as_str(), args, span)) - }; -} - impl<'tcx> LateLintPass<'tcx> for Methods { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { if expr.span.from_expansion() { @@ -2217,7 +2213,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { #[allow(clippy::too_many_lines)] fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Option<&RustcVersion>) { - if let Some((name, [recv, args @ ..], span)) = method_call!(expr) { + if let Some((name, [recv, args @ ..], span)) = method_call(expr) { match (name, args) { ("add" | "offset" | "sub" | "wrapping_offset" | "wrapping_add" | "wrapping_sub", [_arg]) => { zst_offset::check(cx, expr, recv); @@ -2233,7 +2229,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv), ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv), ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, msrv), - ("collect", []) => match method_call!(recv) { + ("collect", []) => match method_call(recv) { Some((name @ ("cloned" | "copied"), [recv2], _)) => { iter_cloned_collect::check(cx, name, expr, recv2); }, @@ -2247,14 +2243,14 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, _ => {}, }, - ("count", []) => match method_call!(recv) { + ("count", []) => match method_call(recv) { Some((name @ ("into_iter" | "iter" | "iter_mut"), [recv2], _)) => { iter_count::check(cx, expr, recv2, name); }, Some(("map", [_, arg], _)) => suspicious_map::check(cx, expr, recv, arg), _ => {}, }, - ("expect", [_]) => match method_call!(recv) { + ("expect", [_]) => match method_call(recv) { Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv), _ => expect_used::check(cx, expr, recv), }, @@ -2271,13 +2267,13 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio flat_map_option::check(cx, expr, arg, span); }, ("flatten", []) => { - if let Some(("map", [recv, map_arg], _)) = method_call!(recv) { + if let Some(("map", [recv, map_arg], _)) = method_call(recv) { map_flatten::check(cx, expr, recv, map_arg); } }, ("fold", [init, acc]) => unnecessary_fold::check(cx, expr, init, acc, span), ("for_each", [_]) => { - if let Some(("inspect", [_, _], span2)) = method_call!(recv) { + if let Some(("inspect", [_, _], span2)) = method_call(recv) { inspect_for_each::check(cx, expr, span2); } }, @@ -2286,7 +2282,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), ("map", [m_arg]) => { - if let Some((name, [recv2, args @ ..], span2)) = method_call!(recv) { + if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) { match (name, args) { ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv), ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, msrv), @@ -2301,7 +2297,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map), ("next", []) => { - if let Some((name, [recv, args @ ..], _)) = method_call!(recv) { + if let Some((name, [recv, args @ ..], _)) = method_call(recv) { match (name, args) { ("filter", [arg]) => filter_next::check(cx, expr, recv, arg), ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv, arg, msrv), @@ -2312,7 +2308,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } } }, - ("nth", [n_arg]) => match method_call!(recv) { + ("nth", [n_arg]) => match method_call(recv) { Some(("bytes", [recv2], _)) => bytes_nth::check(cx, expr, recv2, n_arg), Some(("iter", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, false), Some(("iter_mut", [recv2], _)) => iter_nth::check(cx, expr, recv2, recv, n_arg, true), @@ -2344,12 +2340,12 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => { implicit_clone::check(cx, name, expr, recv, span); }, - ("unwrap", []) => match method_call!(recv) { + ("unwrap", []) => match method_call(recv) { Some(("get", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, false), Some(("get_mut", [recv, get_arg], _)) => get_unwrap::check(cx, expr, recv, get_arg, true), _ => unwrap_used::check(cx, expr, recv), }, - ("unwrap_or", [u_arg]) => match method_call!(recv) { + ("unwrap_or", [u_arg]) => match method_call(recv) { Some((arith @ ("checked_add" | "checked_sub" | "checked_mul"), [lhs, rhs], _)) => { manual_saturating_arithmetic::check(cx, expr, lhs, rhs, u_arg, &arith["checked_".len()..]); }, @@ -2358,7 +2354,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio }, _ => {}, }, - ("unwrap_or_else", [u_arg]) => match method_call!(recv) { + ("unwrap_or_else", [u_arg]) => match method_call(recv) { Some(("map", [recv, map_arg], _)) if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, msrv) => {}, _ => { unwrap_or_else_default::check(cx, expr, recv, u_arg); @@ -2371,7 +2367,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio } fn check_is_some_is_none(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, is_some: bool) { - if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call!(recv) { + if let Some((name @ ("find" | "position" | "rposition"), [f_recv, arg], span)) = method_call(recv) { search_is_some::check(cx, expr, name, is_some, f_recv, arg, recv, span); } } @@ -2535,11 +2531,17 @@ impl SelfKind { implements_trait(cx, ty, trait_def_id, &[parent_ty.into()]) } + fn matches_none<'a>(cx: &LateContext<'a>, parent_ty: Ty<'a>, ty: Ty<'a>) -> bool { + !matches_value(cx, parent_ty, ty) + && !matches_ref(cx, hir::Mutability::Not, parent_ty, ty) + && !matches_ref(cx, hir::Mutability::Mut, parent_ty, ty) + } + match self { Self::Value => matches_value(cx, parent_ty, ty), Self::Ref => matches_ref(cx, hir::Mutability::Not, parent_ty, ty) || ty == parent_ty && is_copy(cx, ty), Self::RefMut => matches_ref(cx, hir::Mutability::Mut, parent_ty, ty), - Self::No => ty != parent_ty, + Self::No => matches_none(cx, parent_ty, ty), } } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 70f20da1d6d..514bdadc442 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -124,7 +124,7 @@ struct IterUsage { } #[allow(clippy::too_many_lines)] -fn parse_iter_usage( +fn parse_iter_usage<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>, @@ -281,7 +281,7 @@ pub(super) fn check_needless_splitn( } } -fn check_iter( +fn check_iter<'tcx>( cx: &LateContext<'tcx>, ctxt: SyntaxContext, mut iter: impl Iterator<Item = (HirId, Node<'tcx>)>, diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 8300df03e99..5999245ea7d 100644 --- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -12,13 +12,13 @@ use rustc_span::{sym, Symbol}; use super::UNNECESSARY_TO_OWNED; -pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, receiver: &'tcx Expr<'tcx>) -> bool { +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); if let Some(callee_def_id) = fn_def_id(cx, parent); if is_into_iter(cx, callee_def_id); then { - check_for_loop_iter(cx, parent, method_name, receiver) + check_for_loop_iter(cx, parent, method_name, receiver, false) } else { false } @@ -30,10 +30,11 @@ pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol /// include this code directly is so that it can be called from /// `unnecessary_into_owned::check_into_iter_call_arg`. pub fn check_for_loop_iter( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, + cx: &LateContext<'_>, + expr: &Expr<'_>, method_name: Symbol, - receiver: &'tcx Expr<'tcx>, + receiver: &Expr<'_>, + cloned_before_iter: bool, ) -> bool { if_chain! { if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent)); @@ -70,12 +71,22 @@ pub fn check_for_loop_iter( expr.span, &format!("unnecessary use of `{}`", method_name), |diag| { - diag.span_suggestion(expr.span, "use", snippet, Applicability::MachineApplicable); + // If `check_into_iter_call_arg` called `check_for_loop_iter` because a call to + // a `to_owned`-like function was removed, then the next suggestion may be + // incorrect. This is because the iterator that results from the call's removal + // could hold a reference to a resource that is used mutably. See + // https://github.com/rust-lang/rust-clippy/issues/8148. + let applicability = if cloned_before_iter { + Applicability::MaybeIncorrect + } else { + Applicability::MachineApplicable + }; + diag.span_suggestion(expr.span, "use", snippet, applicability); for addr_of_expr in addr_of_exprs { match addr_of_expr.kind { ExprKind::AddrOf(_, _, referent) => { let span = addr_of_expr.span.with_hi(referent.span.lo()); - diag.span_suggestion(span, "remove this `&`", String::new(), Applicability::MachineApplicable); + diag.span_suggestion(span, "remove this `&`", String::new(), applicability); } _ => unreachable!(), } @@ -90,7 +101,7 @@ pub fn check_for_loop_iter( /// The core logic of `check_for_loop_iter` above, this function wraps a use of /// `CloneOrCopyVisitor`. -fn clone_or_copy_needed( +fn clone_or_copy_needed<'tcx>( cx: &LateContext<'tcx>, pat: &Pat<'tcx>, body: &'tcx Expr<'tcx>, diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index c48bacfce0d..e5b6d296b2d 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -16,7 +16,7 @@ use std::cmp::max; use super::UNNECESSARY_TO_OWNED; -pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) { +pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, args: &'tcx [Expr<'tcx>]) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); if let [receiver] = args; @@ -44,11 +44,11 @@ pub fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol /// call of a `to_owned`-like function is unnecessary. #[allow(clippy::too_many_lines)] fn check_addr_of_expr( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, + cx: &LateContext<'_>, + expr: &Expr<'_>, method_name: Symbol, method_def_id: DefId, - receiver: &'tcx Expr<'tcx>, + receiver: &Expr<'_>, ) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); @@ -171,12 +171,7 @@ fn check_addr_of_expr( /// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its /// call of a `to_owned`-like function is unnecessary. -fn check_into_iter_call_arg( - cx: &LateContext<'tcx>, - expr: &'tcx Expr<'tcx>, - method_name: Symbol, - receiver: &'tcx Expr<'tcx>, -) -> bool { +fn check_into_iter_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); if let Some(callee_def_id) = fn_def_id(cx, parent); @@ -187,7 +182,13 @@ fn check_into_iter_call_arg( if let Some(item_ty) = get_iterator_item_ty(cx, parent_ty); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); then { - if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver) { + if unnecessary_iter_cloned::check_for_loop_iter( + cx, + parent, + method_name, + receiver, + true, + ) { return true; } let cloned_or_copied = if is_copy(cx, item_ty) { @@ -195,6 +196,9 @@ fn check_into_iter_call_arg( } else { "cloned" }; + // The next suggestion may be incorrect because the removal of the `to_owned`-like + // function could cause the iterator to hold a reference to a resource that is used + // mutably. See https://github.com/rust-lang/rust-clippy/issues/8148. span_lint_and_sugg( cx, UNNECESSARY_TO_OWNED, @@ -202,7 +206,7 @@ fn check_into_iter_call_arg( &format!("unnecessary use of `{}`", method_name), "use", format!("{}.iter().{}()", receiver_snippet, cloned_or_copied), - Applicability::MachineApplicable, + Applicability::MaybeIncorrect, ); return true; } @@ -212,7 +216,7 @@ fn check_into_iter_call_arg( /// Checks whether `expr` is an argument in a function call and, if so, determines whether its call /// of a `to_owned`-like function is unnecessary. -fn check_other_call_arg( +fn check_other_call_arg<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: Symbol, @@ -278,7 +282,7 @@ fn check_other_call_arg( /// Walks an expression's ancestors until it finds a non-`AddrOf` expression. Returns the first such /// expression found (if any) along with the immediately prior expression. -fn skip_addr_of_ancestors( +fn skip_addr_of_ancestors<'tcx>( cx: &LateContext<'tcx>, mut expr: &'tcx Expr<'tcx>, ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>)> { @@ -294,7 +298,7 @@ fn skip_addr_of_ancestors( /// Checks whether an expression is a function or method call and, if so, returns its `DefId`, /// `Substs`, and arguments. -fn get_callee_substs_and_args( +fn get_callee_substs_and_args<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, ) -> Option<(DefId, SubstsRef<'tcx>, &'tcx [Expr<'tcx>])> { @@ -319,7 +323,7 @@ fn get_callee_substs_and_args( } /// Returns the `TraitPredicate`s and `ProjectionPredicate`s for a function's input type. -fn get_input_traits_and_projections( +fn get_input_traits_and_projections<'tcx>( cx: &LateContext<'tcx>, callee_def_id: DefId, input: Ty<'tcx>, @@ -359,7 +363,11 @@ fn get_input_traits_and_projections( } /// Composes two substitutions by applying the latter to the types of the former. -fn compose_substs(cx: &LateContext<'tcx>, left: &[GenericArg<'tcx>], right: SubstsRef<'tcx>) -> Vec<GenericArg<'tcx>> { +fn compose_substs<'tcx>( + cx: &LateContext<'tcx>, + left: &[GenericArg<'tcx>], + right: SubstsRef<'tcx>, +) -> Vec<GenericArg<'tcx>> { left.iter() .map(|arg| { if let GenericArgKind::Type(arg_ty) = arg.unpack() { diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs index 401dc27811d..21b3f81d5d9 100644 --- a/clippy_lints/src/misc.rs +++ b/clippy_lints/src/misc.rs @@ -717,7 +717,7 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>) } } -fn check_binary( +fn check_binary<'a>( cx: &LateContext<'a>, expr: &Expr<'_>, cmp: &rustc_span::source_map::Spanned<rustc_hir::BinOpKind>, diff --git a/clippy_lints/src/mutable_debug_assertion.rs b/clippy_lints/src/mutable_debug_assertion.rs index 12e219cd5c8..842959ce36b 100644 --- a/clippy_lints/src/mutable_debug_assertion.rs +++ b/clippy_lints/src/mutable_debug_assertion.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{higher, is_direct_expn_of}; +use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor}; use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability}; use rustc_lint::{LateContext, LateLintPass}; @@ -34,26 +34,30 @@ declare_clippy_lint! { declare_lint_pass!(DebugAssertWithMutCall => [DEBUG_ASSERT_WITH_MUT_CALL]); -const DEBUG_MACRO_NAMES: [&str; 3] = ["debug_assert", "debug_assert_eq", "debug_assert_ne"]; - impl<'tcx> LateLintPass<'tcx> for DebugAssertWithMutCall { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { - for dmn in &DEBUG_MACRO_NAMES { - if is_direct_expn_of(e.span, dmn).is_some() { - if let Some(macro_args) = higher::extract_assert_macro_args(e) { - for arg in macro_args { - let mut visitor = MutArgVisitor::new(cx); - visitor.visit_expr(arg); - if let Some(span) = visitor.expr_span() { - span_lint( - cx, - DEBUG_ASSERT_WITH_MUT_CALL, - span, - &format!("do not call a function with mutable arguments inside of `{}!`", dmn), - ); - } - } - } + 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" + ) { + return; + } + let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn) else { return }; + for arg in [lhs, rhs] { + let mut visitor = MutArgVisitor::new(cx); + visitor.visit_expr(arg); + if let Some(span) = visitor.expr_span() { + span_lint( + cx, + DEBUG_ASSERT_WITH_MUT_CALL, + span, + &format!( + "do not call a function with mutable arguments inside of `{}!`", + macro_name + ), + ); } } } diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs index 816377fe65e..73823779e49 100644 --- a/clippy_lints/src/mutex_atomic.rs +++ b/clippy_lints/src/mutex_atomic.rs @@ -38,7 +38,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "pre 1.29.0"] pub MUTEX_ATOMIC, - perf, + nursery, "using a mutex where an atomic value could be used instead" } diff --git a/clippy_lints/src/needless_for_each.rs b/clippy_lints/src/needless_for_each.rs index 0c1da035173..19d58f7474b 100644 --- a/clippy_lints/src/needless_for_each.rs +++ b/clippy_lints/src/needless_for_each.rs @@ -48,7 +48,7 @@ declare_clippy_lint! { declare_lint_pass!(NeedlessForEach => [NEEDLESS_FOR_EACH]); -impl LateLintPass<'_> for NeedlessForEach { +impl<'tcx> LateLintPass<'tcx> for NeedlessForEach { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { let expr = match stmt.kind { StmtKind::Expr(expr) | StmtKind::Semi(expr) => expr, diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs index 094a3f111ba..9957afcbf04 100644 --- a/clippy_lints/src/needless_late_init.rs +++ b/clippy_lints/src/needless_late_init.rs @@ -330,7 +330,7 @@ fn check<'tcx>( Some(()) } -impl LateLintPass<'tcx> for NeedlessLateInit { +impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) { let mut parents = cx.tcx.hir().parent_iter(local.hir_id); diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 9d5babc5de8..5bf8a1ba1ca 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect { } } -fn check_no_effect(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) -> bool { +fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { if let StmtKind::Semi(expr) = stmt.kind { if has_no_effect(cx, expr) { span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect"); @@ -155,7 +155,7 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { } } -fn check_unnecessary_operation(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { +fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if_chain! { if let StmtKind::Semi(expr) = stmt.kind; if let Some(reduced) = reduce_expression(cx, expr); diff --git a/clippy_lints/src/non_octal_unix_permissions.rs b/clippy_lints/src/non_octal_unix_permissions.rs index 4b57dbc4c41..e46fee4cac5 100644 --- a/clippy_lints/src/non_octal_unix_permissions.rs +++ b/clippy_lints/src/non_octal_unix_permissions.rs @@ -40,7 +40,7 @@ declare_clippy_lint! { declare_lint_pass!(NonOctalUnixPermissions => [NON_OCTAL_UNIX_PERMISSIONS]); -impl LateLintPass<'_> for NonOctalUnixPermissions { +impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { match &expr.kind { ExprKind::MethodCall(path, _, [func, param], _) => { diff --git a/clippy_lints/src/octal_escapes.rs b/clippy_lints/src/octal_escapes.rs index 9c971437645..e0da12f77fc 100644 --- a/clippy_lints/src/octal_escapes.rs +++ b/clippy_lints/src/octal_escapes.rs @@ -50,7 +50,7 @@ declare_clippy_lint! { declare_lint_pass!(OctalEscapes => [OCTAL_ESCAPES]); impl EarlyLintPass for OctalEscapes { - fn check_expr(&mut self, cx: &EarlyContext<'tcx>, expr: &Expr) { + fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if in_external_macro(cx.sess, expr.span) { return; } @@ -65,7 +65,7 @@ impl EarlyLintPass for OctalEscapes { } } -fn check_lit(cx: &EarlyContext<'tcx>, lit: &Lit, span: Span, is_string: bool) { +fn check_lit(cx: &EarlyContext<'_>, lit: &Lit, span: Span, is_string: bool) { let contents = lit.symbol.as_str(); let mut iter = contents.char_indices().peekable(); let mut found = vec![]; diff --git a/clippy_lints/src/panic_in_result_fn.rs b/clippy_lints/src/panic_in_result_fn.rs index 8769c045214..b7a56970b33 100644 --- a/clippy_lints/src/panic_in_result_fn.rs +++ b/clippy_lints/src/panic_in_result_fn.rs @@ -1,8 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::macros::root_macro_call_first_node; +use clippy_utils::return_ty; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{find_macro_calls, is_expn_of, return_ty}; +use clippy_utils::visitors::expr_visitor_no_bodies; use rustc_hir as hir; -use rustc_hir::intravisit::FnKind; +use rustc_hir::intravisit::{FnKind, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::{sym, Span}; @@ -55,19 +57,19 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { } fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { - let mut panics = find_macro_calls( - &[ - "unimplemented", - "unreachable", - "panic", - "todo", - "assert", - "assert_eq", - "assert_ne", - ], - body, - ); - panics.retain(|span| is_expn_of(*span, "debug_assert").is_none()); + let mut panics = Vec::new(); + expr_visitor_no_bodies(|expr| { + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return true }; + if matches!( + &*cx.tcx.item_name(macro_call.def_id).as_str(), + "unimplemented" | "unreachable" | "panic" | "todo" | "assert" | "assert_eq" | "assert_ne" + ) { + panics.push(macro_call.span); + return false; + } + true + }) + .visit_expr(&body.value); if !panics.is_empty() { span_lint_and_then( cx, diff --git a/clippy_lints/src/panic_unimplemented.rs b/clippy_lints/src/panic_unimplemented.rs index edfac824ded..6ef6b9a20aa 100644 --- a/clippy_lints/src/panic_unimplemented.rs +++ b/clippy_lints/src/panic_unimplemented.rs @@ -1,10 +1,8 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::{is_expn_of, match_panic_call}; -use if_chain::if_chain; +use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use rustc_hir::Expr; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_lint_pass, declare_tool_lint}; -use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -78,37 +76,37 @@ declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANI impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if match_panic_call(cx, expr).is_some() - && (is_expn_of(expr.span, "debug_assert").is_none() && is_expn_of(expr.span, "assert").is_none()) - { - let span = get_outer_span(expr); - if is_expn_of(expr.span, "unimplemented").is_some() { + let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return }; + if is_panic(cx, macro_call.def_id) { + span_lint( + cx, + PANIC, + macro_call.span, + "`panic` should not be present in production code", + ); + return; + } + match cx.tcx.item_name(macro_call.def_id).as_str() { + "todo" => { + span_lint( + cx, + TODO, + macro_call.span, + "`todo` should not be present in production code", + ); + }, + "unimplemented" => { span_lint( cx, UNIMPLEMENTED, - span, + macro_call.span, "`unimplemented` should not be present in production code", ); - } else if is_expn_of(expr.span, "todo").is_some() { - span_lint(cx, TODO, span, "`todo` should not be present in production code"); - } else if is_expn_of(expr.span, "unreachable").is_some() { - span_lint(cx, UNREACHABLE, span, "usage of the `unreachable!` macro"); - } else if is_expn_of(expr.span, "panic").is_some() { - span_lint(cx, PANIC, span, "`panic` should not be present in production code"); - } - } - } -} - -fn get_outer_span(expr: &Expr<'_>) -> Span { - if_chain! { - if expr.span.from_expansion(); - let first = expr.span.ctxt().outer_expn_data().call_site; - if first.from_expansion(); - then { - first.ctxt().outer_expn_data().call_site - } else { - expr.span + }, + "unreachable" => { + span_lint(cx, UNREACHABLE, macro_call.span, "usage of the `unreachable!` macro"); + }, + _ => {}, } } } diff --git a/clippy_lints/src/ptr_eq.rs b/clippy_lints/src/ptr_eq.rs index 3c126fc1ca6..2bec93ac606 100644 --- a/clippy_lints/src/ptr_eq.rs +++ b/clippy_lints/src/ptr_eq.rs @@ -39,7 +39,7 @@ declare_lint_pass!(PtrEq => [PTR_EQ]); static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers"; -impl LateLintPass<'_> for PtrEq { +impl<'tcx> LateLintPass<'tcx> for PtrEq { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if expr.span.from_expansion() { return; diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs index 1cf349f8aa7..1991a01fb60 100644 --- a/clippy_lints/src/redundant_clone.rs +++ b/clippy_lints/src/redundant_clone.rs @@ -220,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { continue; } else if let Some(loc) = clone_usage.cloned_consume_or_mutate_loc { // cloned value is mutated, and the clone is alive. - if possible_borrower.is_alive_at(ret_local, loc) { + if possible_borrower.local_is_alive_at(ret_local, loc) { continue; } } @@ -767,7 +767,7 @@ impl PossibleBorrowerMap<'_, '_> { self.bitset.0 == self.bitset.1 } - fn is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { + fn local_is_alive_at(&mut self, local: mir::Local, at: mir::Location) -> bool { self.maybe_live.seek_after_primary_effect(at); self.maybe_live.contains(local) } diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs index b2bd0103d11..7c88b42ea31 100644 --- a/clippy_lints/src/redundant_slicing.rs +++ b/clippy_lints/src/redundant_slicing.rs @@ -42,7 +42,7 @@ declare_clippy_lint! { declare_lint_pass!(RedundantSlicing => [REDUNDANT_SLICING]); -impl LateLintPass<'_> for RedundantSlicing { +impl<'tcx> LateLintPass<'tcx> for RedundantSlicing { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if expr.span.from_expansion() { return; diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs index 22ae7a291d0..b2448372370 100644 --- a/clippy_lints/src/reference.rs +++ b/clippy_lints/src/reference.rs @@ -50,6 +50,7 @@ impl EarlyLintPass for DerefAddrOf { if_chain! { if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind; if let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind; + if deref_target.span.ctxt() == e.span.ctxt(); if !addrof_target.span.from_expansion(); then { let mut applicability = Applicability::MachineApplicable; diff --git a/clippy_lints/src/return_self_not_must_use.rs b/clippy_lints/src/return_self_not_must_use.rs index b57ec96bc7e..5dafd08cf3b 100644 --- a/clippy_lints/src/return_self_not_must_use.rs +++ b/clippy_lints/src/return_self_not_must_use.rs @@ -1,5 +1,6 @@ +use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::ty::is_must_use_ty; -use clippy_utils::{diagnostics::span_lint, nth_arg, return_ty}; +use clippy_utils::{nth_arg, return_ty}; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl, HirId, TraitItem, TraitItemKind}; @@ -13,25 +14,46 @@ declare_clippy_lint! { /// This lint warns when a method returning `Self` doesn't have the `#[must_use]` attribute. /// /// ### Why is this bad? - /// It prevents to "forget" to use the newly created value. + /// Methods returning `Self` often create new values, having the `#[must_use]` attribute + /// prevents users from "forgetting" to use the newly created value. + /// + /// The `#[must_use]` attribute can be added to the type itself to ensure that instances + /// are never forgotten. Functions returning a type marked with `#[must_use]` will not be + /// linted, as the usage is already enforced by the type attribute. /// /// ### Limitations /// This lint is only applied on methods taking a `self` argument. It would be mostly noise /// if it was added on constructors for example. /// /// ### Example + /// Missing attribute /// ```rust /// pub struct Bar; - /// /// impl Bar { /// // Bad /// pub fn bar(&self) -> Self { /// Self /// } + /// } + /// ``` /// - /// // Good + /// It's better to have the `#[must_use]` attribute on the method like this: + /// ```rust + /// pub struct Bar; + /// impl Bar { /// #[must_use] - /// pub fn foo(&self) -> Self { + /// pub fn bar(&self) -> Self { + /// Self + /// } + /// } + /// ``` + /// + /// Or on the type definition like this: + /// ```rust + /// #[must_use] + /// pub struct Bar; + /// impl Bar { + /// pub fn bar(&self) -> Self { /// Self /// } /// } @@ -44,7 +66,7 @@ declare_clippy_lint! { declare_lint_pass!(ReturnSelfNotMustUse => [RETURN_SELF_NOT_MUST_USE]); -fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalDefId, span: Span, hir_id: HirId) { +fn check_method(cx: &LateContext<'_>, decl: &FnDecl<'_>, fn_def: LocalDefId, span: Span, hir_id: HirId) { if_chain! { // If it comes from an external macro, better ignore it. if !in_external_macro(cx.sess(), span); @@ -65,11 +87,13 @@ fn check_method(cx: &LateContext<'tcx>, decl: &'tcx FnDecl<'tcx>, fn_def: LocalD if !is_must_use_ty(cx, ret_ty); then { - span_lint( + span_lint_and_help( cx, RETURN_SELF_NOT_MUST_USE, span, "missing `#[must_use]` attribute on a method returning `Self`", + None, + "consider adding the `#[must_use]` attribute to the method or directly to the `Self` type" ); } } diff --git a/clippy_lints/src/semicolon_if_nothing_returned.rs b/clippy_lints/src/semicolon_if_nothing_returned.rs index 0b3bbbc8155..729694da46d 100644 --- a/clippy_lints/src/semicolon_if_nothing_returned.rs +++ b/clippy_lints/src/semicolon_if_nothing_returned.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { declare_lint_pass!(SemicolonIfNothingReturned => [SEMICOLON_IF_NOTHING_RETURNED]); -impl LateLintPass<'_> for SemicolonIfNothingReturned { +impl<'tcx> LateLintPass<'tcx> for SemicolonIfNothingReturned { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { if_chain! { if !block.span.from_expansion(); diff --git a/clippy_lints/src/single_char_lifetime_names.rs b/clippy_lints/src/single_char_lifetime_names.rs new file mode 100644 index 00000000000..ee82666b5af --- /dev/null +++ b/clippy_lints/src/single_char_lifetime_names.rs @@ -0,0 +1,63 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_ast::ast::{GenericParam, GenericParamKind}; +use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for lifetimes with names which are one character + /// long. + /// + /// ### Why is this bad? + /// A single character is likely not enough to express the + /// purpose of a lifetime. Using a longer name can make code + /// easier to understand, especially for those who are new to + /// Rust. + /// + /// ### Known problems + /// Rust programmers and learning resources tend to use single + /// character lifetimes, so this lint is at odds with the + /// ecosystem at large. In addition, the lifetime's purpose may + /// be obvious or, rarely, expressible in one character. + /// + /// ### Example + /// ```rust + /// struct DiagnosticCtx<'a> { + /// source: &'a str, + /// } + /// ``` + /// Use instead: + /// ```rust + /// struct DiagnosticCtx<'src> { + /// source: &'src str, + /// } + /// ``` + #[clippy::version = "1.59.0"] + pub SINGLE_CHAR_LIFETIME_NAMES, + restriction, + "warns against single-character lifetime names" +} + +declare_lint_pass!(SingleCharLifetimeNames => [SINGLE_CHAR_LIFETIME_NAMES]); + +impl EarlyLintPass for SingleCharLifetimeNames { + fn check_generic_param(&mut self, ctx: &EarlyContext<'_>, param: &GenericParam) { + if in_external_macro(ctx.sess, param.ident.span) { + return; + } + + if let GenericParamKind::Lifetime = param.kind { + if !param.is_placeholder && param.ident.as_str().len() <= 2 { + span_lint_and_help( + ctx, + SINGLE_CHAR_LIFETIME_NAMES, + param.ident.span, + "single-character lifetime names are likely uninformative", + None, + "use a more informative name", + ); + } + } + } +} diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs index df1e85afdd7..9b195f3c0a2 100644 --- a/clippy_lints/src/size_of_in_element_count.rs +++ b/clippy_lints/src/size_of_in_element_count.rs @@ -37,7 +37,7 @@ declare_clippy_lint! { declare_lint_pass!(SizeOfInElementCount => [SIZE_OF_IN_ELEMENT_COUNT]); -fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> { +fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) -> Option<Ty<'tcx>> { match expr.kind { ExprKind::Call(count_func, _func_args) => { if_chain! { @@ -64,7 +64,10 @@ fn get_size_of_ty(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, inverted: bool) } } -fn get_pointee_ty_and_count_expr(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { +fn get_pointee_ty_and_count_expr<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'_>, +) -> Option<(Ty<'tcx>, &'tcx Expr<'tcx>)> { const FUNCTIONS: [&[&str]; 8] = [ &paths::PTR_COPY_NONOVERLAPPING, &paths::PTR_COPY, diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs index ad8e72ad764..b4a71aefd43 100644 --- a/clippy_lints/src/strings.rs +++ b/clippy_lints/src/strings.rs @@ -381,7 +381,7 @@ declare_clippy_lint! { declare_lint_pass!(StrToString => [STR_TO_STRING]); -impl LateLintPass<'_> for StrToString { +impl<'tcx> LateLintPass<'tcx> for StrToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if_chain! { if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind; @@ -431,7 +431,7 @@ declare_clippy_lint! { declare_lint_pass!(StringToString => [STRING_TO_STRING]); -impl LateLintPass<'_> for StringToString { +impl<'tcx> LateLintPass<'tcx> for StringToString { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { if_chain! { if let ExprKind::MethodCall(path, _, [self_arg, ..], _) = &expr.kind; diff --git a/clippy_lints/src/strlen_on_c_strings.rs b/clippy_lints/src/strlen_on_c_strings.rs index fee01fb0bd1..d6e948a7560 100644 --- a/clippy_lints/src/strlen_on_c_strings.rs +++ b/clippy_lints/src/strlen_on_c_strings.rs @@ -39,7 +39,7 @@ declare_clippy_lint! { declare_lint_pass!(StrlenOnCStrings => [STRLEN_ON_C_STRINGS]); -impl LateLintPass<'tcx> for StrlenOnCStrings { +impl<'tcx> LateLintPass<'tcx> for StrlenOnCStrings { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if_chain! { if !expr.span.from_expansion(); diff --git a/clippy_lints/src/suspicious_operation_groupings.rs b/clippy_lints/src/suspicious_operation_groupings.rs index faf43fd9fc1..ca725918e87 100644 --- a/clippy_lints/src/suspicious_operation_groupings.rs +++ b/clippy_lints/src/suspicious_operation_groupings.rs @@ -355,7 +355,7 @@ struct BinaryOp<'exprs> { right: &'exprs Expr, } -impl BinaryOp<'exprs> { +impl<'exprs> BinaryOp<'exprs> { fn new(op: BinOpKind, span: Span, (left, right): (&'exprs Expr, &'exprs Expr)) -> Self { Self { op, span, left, right } } @@ -419,7 +419,7 @@ fn chained_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> { } } -fn chained_binops_helper(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option<Vec<BinaryOp<'expr>>> { +fn chained_binops_helper<'expr>(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option<Vec<BinaryOp<'expr>>> { match (&left_outer.kind, &right_outer.kind) { ( ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), diff --git a/clippy_lints/src/trailing_empty_array.rs b/clippy_lints/src/trailing_empty_array.rs index af36f726700..c9b2ce476e8 100644 --- a/clippy_lints/src/trailing_empty_array.rs +++ b/clippy_lints/src/trailing_empty_array.rs @@ -53,13 +53,12 @@ impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray { } } -fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) -> bool { +fn is_struct_with_trailing_zero_sized_array(cx: &LateContext<'_>, item: &Item<'_>) -> bool { if_chain! { // First check if last field is an array if let ItemKind::Struct(data, _) = &item.kind; if let Some(last_field) = data.fields().last(); - if let rustc_hir::TyKind::Array(_, length) = last_field.ty.kind; - if let rustc_hir::ArrayLen::Body(length) = length; + if let rustc_hir::TyKind::Array(_, rustc_hir::ArrayLen::Body(length)) = last_field.ty.kind; // Then check if that that array zero-sized let length_ldid = cx.tcx.hir().local_def_id(length.hir_id); diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs index fb4abceac25..6369aafe3f9 100644 --- a/clippy_lints/src/trait_bounds.rs +++ b/clippy_lints/src/trait_bounds.rs @@ -1,11 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::{snippet, snippet_with_applicability}; -use clippy_utils::SpanlessHash; +use clippy_utils::{SpanlessEq, SpanlessHash}; +use core::hash::{Hash, Hasher}; use if_chain::if_chain; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::unhash::UnhashMap; use rustc_errors::Applicability; -use rustc_hir::{def::Res, GenericBound, Generics, ParamName, Path, QPath, TyKind, WherePredicate}; +use rustc_hir::def::Res; +use rustc_hir::{ + GenericBound, Generics, Item, ItemKind, Node, ParamName, Path, PathSegment, QPath, TraitItem, Ty, TyKind, + WherePredicate, +}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -83,6 +88,53 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { self.check_type_repetition(cx, gen); check_trait_bound_duplication(cx, gen); } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) { + let Generics { where_clause, .. } = &item.generics; + let mut self_bounds_set = FxHashSet::default(); + + for predicate in where_clause.predicates { + if_chain! { + if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate; + if !bound_predicate.span.from_expansion(); + if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind; + if let Some(PathSegment { res: Some(Res::SelfTy(Some(def_id), _)), .. }) = segments.first(); + + if let Some( + Node::Item( + Item { + kind: ItemKind::Trait(_, _, _, self_bounds, _), + .. } + ) + ) = cx.tcx.hir().get_if_local(*def_id); + then { + if self_bounds_set.is_empty() { + for bound in self_bounds.iter() { + let Some((self_res, _)) = get_trait_res_span_from_bound(bound) else { continue }; + self_bounds_set.insert(self_res); + } + } + + bound_predicate + .bounds + .iter() + .filter_map(get_trait_res_span_from_bound) + .for_each(|(trait_item_res, span)| { + if self_bounds_set.get(&trait_item_res).is_some() { + span_lint_and_help( + cx, + TRAIT_DUPLICATION_IN_BOUNDS, + span, + "this trait bound is already specified in trait declaration", + None, + "consider removing this trait bound", + ); + } + }); + } + } + } + } } fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span)> { @@ -94,24 +146,40 @@ fn get_trait_res_span_from_bound(bound: &GenericBound<'_>) -> Option<(Res, Span) } impl TraitBounds { - fn check_type_repetition(self, cx: &LateContext<'_>, gen: &'_ Generics<'_>) { + fn check_type_repetition<'tcx>(self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) { + struct SpanlessTy<'cx, 'tcx> { + ty: &'tcx Ty<'tcx>, + cx: &'cx LateContext<'tcx>, + } + impl PartialEq for SpanlessTy<'_, '_> { + fn eq(&self, other: &Self) -> bool { + let mut eq = SpanlessEq::new(self.cx); + eq.inter_expr().eq_ty(self.ty, other.ty) + } + } + impl Hash for SpanlessTy<'_, '_> { + fn hash<H: Hasher>(&self, h: &mut H) { + let mut t = SpanlessHash::new(self.cx); + t.hash_ty(self.ty); + h.write_u64(t.finish()); + } + } + impl Eq for SpanlessTy<'_, '_> {} + if gen.span.from_expansion() { return; } - let hash = |ty| -> u64 { - let mut hasher = SpanlessHash::new(cx); - hasher.hash_ty(ty); - hasher.finish() - }; - let mut map: UnhashMap<u64, Vec<&GenericBound<'_>>> = UnhashMap::default(); + let mut map: UnhashMap<SpanlessTy<'_, '_>, Vec<&GenericBound<'_>>> = UnhashMap::default(); let mut applicability = Applicability::MaybeIncorrect; for bound in gen.where_clause.predicates { if_chain! { if let WherePredicate::BoundPredicate(ref p) = bound; if p.bounds.len() as u64 <= self.max_trait_bounds; if !p.span.from_expansion(); - let h = hash(p.bounded_ty); - if let Some(ref v) = map.insert(h, p.bounds.iter().collect::<Vec<_>>()); + if let Some(ref v) = map.insert( + SpanlessTy { ty: p.bounded_ty, cx }, + p.bounds.iter().collect::<Vec<_>>() + ); then { let mut hint_string = format!( diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 481e5957435..9d57505e55e 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -167,8 +167,9 @@ declare_clippy_lint! { /// Check the [Box documentation](https://doc.rust-lang.org/std/boxed/index.html) for more information. /// /// ### Why is this bad? - /// Any `&Box<T>` can also be a `&T`, which is more - /// general. + /// A `&Box<T>` parameter requires the function caller to box `T` first before passing it to a function. + /// Using `&T` defines a concrete type for the parameter and generalizes the function, this would also + /// auto-deref to `&T` at the function call site if passed a `&Box<T>`. /// /// ### Example /// ```rust,ignore diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index 3d3b4a6679d..697ed267e2f 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -113,8 +113,8 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { } } -impl<'hir> Visitor<'hir> for UndocumentedUnsafeBlocks { - type Map = Map<'hir>; +impl<'v> Visitor<'v> for UndocumentedUnsafeBlocks { + type Map = Map<'v>; fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { NestedVisitorMap::None diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs index c58fa67a023..7557e14d11f 100644 --- a/clippy_lints/src/undropped_manually_drops.rs +++ b/clippy_lints/src/undropped_manually_drops.rs @@ -36,7 +36,7 @@ declare_clippy_lint! { declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]); -impl LateLintPass<'tcx> for UndroppedManuallyDrops { +impl<'tcx> LateLintPass<'tcx> for UndroppedManuallyDrops { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let Some([arg_0, ..]) = match_function_call(cx, expr, &paths::DROP) { let ty = cx.typeck_results().expr_ty(arg_0); diff --git a/clippy_lints/src/uninit_vec.rs b/clippy_lints/src/uninit_vec.rs index 46cc76b150e..2ffaf24f942 100644 --- a/clippy_lints/src/uninit_vec.rs +++ b/clippy_lints/src/uninit_vec.rs @@ -78,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for UninitVec { } } -fn handle_uninit_vec_pair( +fn handle_uninit_vec_pair<'tcx>( cx: &LateContext<'tcx>, maybe_init_or_reserve: &'tcx Stmt<'tcx>, maybe_set_len: &'tcx Expr<'tcx>, @@ -196,7 +196,7 @@ fn is_reserve(cx: &LateContext<'_>, path: &PathSegment<'_>, self_expr: &Expr<'_> } /// Returns self if the expression is `Vec::set_len()` -fn extract_set_len_self(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&'tcx Expr<'tcx>, Span)> { +fn extract_set_len_self<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Option<(&'tcx Expr<'tcx>, Span)> { // peel unsafe blocks in `unsafe { vec.set_len() }` let expr = peel_hir_expr_while(expr, |e| { if let ExprKind::Block(block, _) = e.kind { diff --git a/clippy_lints/src/unit_hash.rs b/clippy_lints/src/unit_hash.rs index 26b4e0f58a8..dcf8a9d7c84 100644 --- a/clippy_lints/src/unit_hash.rs +++ b/clippy_lints/src/unit_hash.rs @@ -46,7 +46,7 @@ declare_clippy_lint! { } declare_lint_pass!(UnitHash => [UNIT_HASH]); -impl LateLintPass<'tcx> for UnitHash { +impl<'tcx> LateLintPass<'tcx> for UnitHash { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { if_chain! { if let ExprKind::MethodCall(name_ident, _, args, _) = &expr.kind; diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs index 6d9aff47421..1dd8895ebd0 100644 --- a/clippy_lints/src/unit_types/unit_cmp.rs +++ b/clippy_lints/src/unit_types/unit_cmp.rs @@ -1,35 +1,29 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node}; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::LateContext; -use rustc_span::hygiene::{ExpnKind, MacroKind}; use super::UNIT_CMP; pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { if expr.span.from_expansion() { - if let Some(callee) = expr.span.source_callee() { - if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind { - if let ExprKind::Binary(ref cmp, left, _) = expr.kind { - let op = cmp.node; - if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() { - let result = match symbol.as_str() { - "assert_eq" | "debug_assert_eq" => "succeed", - "assert_ne" | "debug_assert_ne" => "fail", - _ => return, - }; - span_lint( - cx, - UNIT_CMP, - expr.span, - &format!( - "`{}` of unit values detected. This will always {}", - symbol.as_str(), - result - ), - ); - } - } + 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", + _ => return, + }; + let Some ((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else { return }; + if !cx.typeck_results().expr_ty(left).is_unit() { + return; } + span_lint( + cx, + UNIT_CMP, + macro_call.span, + &format!("`{}` of unit values detected. This will always {}", macro_name, result), + ); } return; } diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs index 004530db086..287ac5b4a90 100644 --- a/clippy_lints/src/unused_io_amount.rs +++ b/clippy_lints/src/unused_io_amount.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::span_lint; +use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::{is_try, match_trait_method, paths}; use rustc_hir as hir; use rustc_lint::{LateContext, LateLintPass}; @@ -17,10 +17,17 @@ declare_clippy_lint! { /// partial-write/read, use /// `write_all`/`read_exact` instead. /// + /// When working with asynchronous code (either with the `futures` + /// crate or with `tokio`), a similar issue exists for + /// `AsyncWriteExt::write()` and `AsyncReadExt::read()` : these + /// functions are also not guaranteed to process the entire + /// buffer. Your code should either handle partial-writes/reads, or + /// call the `write_all`/`read_exact` methods on those traits instead. + /// /// ### Known problems /// Detects only common patterns. /// - /// ### Example + /// ### Examples /// ```rust,ignore /// use std::io; /// fn foo<W: io::Write>(w: &mut W) -> io::Result<()> { @@ -68,6 +75,23 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount { } } +/// If `expr` is an (e).await, return the inner expression "e" that's being +/// waited on. Otherwise return None. +fn try_remove_await<'a>(expr: &'a hir::Expr<'a>) -> Option<&hir::Expr<'a>> { + if let hir::ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind { + if let hir::ExprKind::Call(func, [ref arg_0, ..]) = expr.kind { + if matches!( + func.kind, + hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..)) + ) { + return Some(arg_0); + } + } + } + + None +} + fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { let mut call = call; while let hir::ExprKind::MethodCall(path, _, args, _) = call.kind { @@ -77,30 +101,69 @@ fn check_map_error(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr< break; } } - check_method_call(cx, call, expr); + + if let Some(call) = try_remove_await(call) { + check_method_call(cx, call, expr, true); + } else { + check_method_call(cx, call, expr, false); + } } -fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>) { +fn check_method_call(cx: &LateContext<'_>, call: &hir::Expr<'_>, expr: &hir::Expr<'_>, is_await: bool) { if let hir::ExprKind::MethodCall(path, _, _, _) = call.kind { let symbol = path.ident.as_str(); - let read_trait = match_trait_method(cx, call, &paths::IO_READ); - let write_trait = match_trait_method(cx, call, &paths::IO_WRITE); + let read_trait = if is_await { + match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT) + || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT) + } else { + match_trait_method(cx, call, &paths::IO_READ) + }; + let write_trait = if is_await { + match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT) + || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT) + } else { + match_trait_method(cx, call, &paths::IO_WRITE) + }; - match (read_trait, write_trait, symbol) { - (true, _, "read") => span_lint( + match (read_trait, write_trait, symbol, is_await) { + (true, _, "read", false) => span_lint_and_help( + cx, + UNUSED_IO_AMOUNT, + expr.span, + "read amount is not handled", + None, + "use `Read::read_exact` instead, or handle partial reads", + ), + (true, _, "read", true) => span_lint_and_help( cx, UNUSED_IO_AMOUNT, expr.span, - "read amount is not handled. Use `Read::read_exact` instead", + "read amount is not handled", + None, + "use `AsyncReadExt::read_exact` instead, or handle partial reads", ), - (true, _, "read_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"), - (_, true, "write") => span_lint( + (true, _, "read_vectored", _) => { + span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "read amount is not handled"); + }, + (_, true, "write", false) => span_lint_and_help( cx, UNUSED_IO_AMOUNT, expr.span, - "written amount is not handled. Use `Write::write_all` instead", + "written amount is not handled", + None, + "use `Write::write_all` instead, or handle partial writes", ), - (_, true, "write_vectored") => span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"), + (_, true, "write", true) => span_lint_and_help( + cx, + UNUSED_IO_AMOUNT, + expr.span, + "written amount is not handled", + None, + "use `AsyncWriteExt::write_all` instead, or handle partial writes", + ), + (_, true, "write_vectored", _) => { + span_lint(cx, UNUSED_IO_AMOUNT, expr.span, "written amount is not handled"); + }, _ => (), } } diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs index 9b06ca4e824..7751c593e43 100644 --- a/clippy_lints/src/utils/author.rs +++ b/clippy_lints/src/utils/author.rs @@ -573,7 +573,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { bind!(self, anon_const); out!("if let ArrayLen::Body({anon_const}) = {length};"); self.body(field!(anon_const.body)); - } + }, } }, ExprKind::Err => kind!("Err"), diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index 9c83d30eb9c..d6deb50cc90 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -23,6 +23,14 @@ pub enum DisallowedMethod { WithReason { path: String, reason: Option<String> }, } +impl DisallowedMethod { + pub fn path(&self) -> &str { + let (Self::Simple(path) | Self::WithReason { path, .. }) = self; + + path + } +} + /// A single disallowed type, used by the `DISALLOWED_TYPES` lint. #[derive(Clone, Debug, Deserialize)] #[serde(untagged)] @@ -113,7 +121,7 @@ macro_rules! define_Conf { } } - #[cfg(feature = "metadata-collector-lint")] + #[cfg(feature = "internal")] pub mod metadata { use crate::utils::internal_lints::metadata_collector::ClippyConfiguration; diff --git a/clippy_lints/src/utils/inspector.rs b/clippy_lints/src/utils/inspector.rs index c96766e5678..e90b6b73b34 100644 --- a/clippy_lints/src/utils/inspector.rs +++ b/clippy_lints/src/utils/inspector.rs @@ -342,8 +342,8 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) { match length { hir::ArrayLen::Infer(_, _) => println!("{}repeat count: _", ind), hir::ArrayLen::Body(anon_const) => { - print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1) - } + print_expr(cx, &cx.tcx.hir().body(anon_const.body).value, indent + 1); + }, } }, hir::ExprKind::Err => { diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs index 7d196af7a53..9c3dcc8e96a 100644 --- a/clippy_lints/src/utils/internal_lints.rs +++ b/clippy_lints/src/utils/internal_lints.rs @@ -1,5 +1,6 @@ use clippy_utils::consts::{constant_simple, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::macros::root_macro_call_first_node; use clippy_utils::source::snippet; use clippy_utils::ty::match_type; use clippy_utils::{ @@ -34,7 +35,7 @@ use rustc_typeck::hir_ty_to_ty; use std::borrow::{Borrow, Cow}; -#[cfg(feature = "metadata-collector-lint")] +#[cfg(feature = "internal")] pub mod metadata_collector; declare_clippy_lint! { @@ -410,9 +411,13 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass { } self.declared_lints.insert(item.ident.name, item.span); } - } else if is_expn_of(item.span, "impl_lint_pass").is_some() - || is_expn_of(item.span, "declare_lint_pass").is_some() - { + } else if let Some(macro_call) = root_macro_call_first_node(cx, item) { + if !matches!( + &*cx.tcx.item_name(macro_call.def_id).as_str(), + "impl_lint_pass" | "declare_lint_pass" + ) { + return; + } if let hir::ItemKind::Impl(hir::Impl { of_trait: None, items: impl_item_refs, @@ -924,9 +929,20 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool { let lang_item_path = cx.get_def_path(*item_def_id); if path_syms.starts_with(&lang_item_path) { if let [item] = &path_syms[lang_item_path.len()..] { - for child in cx.tcx.module_children(*item_def_id) { - if child.ident.name == *item { - return true; + if matches!( + cx.tcx.def_kind(*item_def_id), + DefKind::Mod | DefKind::Enum | DefKind::Trait + ) { + for child in cx.tcx.module_children(*item_def_id) { + if child.ident.name == *item { + return true; + } + } + } else { + for child in cx.tcx.associated_item_def_ids(*item_def_id) { + if cx.tcx.item_name(*child) == *item { + return true; + } } } } diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs index 7707eebd622..4e46d79dc08 100644 --- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs +++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs @@ -1,8 +1,7 @@ //! This lint is used to collect metadata about clippy lints. This metadata is exported as a json //! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html) //! -//! This module and therefor the entire lint is guarded by a feature flag called -//! `metadata-collector-lint` +//! This module and therefore the entire lint is guarded by a feature flag called `internal` //! //! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such @@ -578,7 +577,7 @@ fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String { fn get_lint_group_and_level_or_lint( cx: &LateContext<'_>, lint_name: &str, - item: &'hir Item<'_>, + item: &Item<'_>, ) -> Option<(String, &'static str)> { let result = cx .lint_store @@ -697,20 +696,20 @@ fn extract_emission_info<'hir>( } /// Resolves the possible lints that this expression could reference -fn resolve_lints(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> { +fn resolve_lints<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> { let mut resolver = LintResolver::new(cx); resolver.visit_expr(expr); resolver.lints } /// This function tries to resolve the linked applicability to the given expression. -fn resolve_applicability(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> { +fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> { let mut resolver = ApplicabilityResolver::new(cx); resolver.visit_expr(expr); resolver.complete() } -fn check_is_multi_part(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool { +fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool { if let ExprKind::Closure(_, _, body_id, _, _) = closure_expr.kind { let mut scanner = IsMultiSpanScanner::new(cx); intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body_id)); @@ -825,7 +824,7 @@ impl<'a, 'hir> intravisit::Visitor<'hir> for ApplicabilityResolver<'a, 'hir> { } /// This returns the parent local node if the expression is a reference one -fn get_parent_local(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> { +fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::Local<'hir>> { if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind { if let hir::def::Res::Local(local_hir) = path.res { return get_parent_local_hir_id(cx, local_hir); @@ -835,7 +834,7 @@ fn get_parent_local(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Opti None } -fn get_parent_local_hir_id(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { +fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::Local<'hir>> { let map = cx.tcx.hir(); match map.find(map.get_parent_node(hir_id)) { diff --git a/clippy_lints/src/utils/mod.rs b/clippy_lints/src/utils/mod.rs index b67448e3a57..dc385ebacba 100644 --- a/clippy_lints/src/utils/mod.rs +++ b/clippy_lints/src/utils/mod.rs @@ -1,5 +1,5 @@ pub mod author; pub mod conf; pub mod inspector; -#[cfg(any(feature = "internal-lints", feature = "metadata-collector-lint"))] +#[cfg(feature = "internal")] pub mod internal_lints; diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index 1bc0eb6303c..43474da3450 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -77,7 +77,7 @@ impl VecPushSearcher { } } -impl LateLintPass<'_> for VecInitThenPush { +impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { fn check_block(&mut self, _: &LateContext<'tcx>, _: &'tcx Block<'tcx>) { self.searcher = None; } |
