diff options
| author | Philipp Krones <hello@philkrones.com> | 2022-12-01 18:29:38 +0100 |
|---|---|---|
| committer | Philipp Krones <hello@philkrones.com> | 2022-12-01 18:29:38 +0100 |
| commit | d05e2865a05e86de9cfd283d4a6f88340346f48b (patch) | |
| tree | 58b078a75250ad3e23dd9b3022412a849f009e56 /clippy_lints | |
| parent | 58100c014a815f7bc30d58f27e4283166146fbbb (diff) | |
| download | rust-d05e2865a05e86de9cfd283d4a6f88340346f48b.tar.gz rust-d05e2865a05e86de9cfd283d4a6f88340346f48b.zip | |
Merge commit 'd822110d3b5625b9dc80ccc442e06fc3cc851d76' into clippyup
Diffstat (limited to 'clippy_lints')
68 files changed, 1034 insertions, 612 deletions
diff --git a/clippy_lints/src/almost_complete_letter_range.rs b/clippy_lints/src/almost_complete_letter_range.rs index df92579a85d..52beaf504a4 100644 --- a/clippy_lints/src/almost_complete_letter_range.rs +++ b/clippy_lints/src/almost_complete_letter_range.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{trim_span, walk_span_to_context}; -use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -33,10 +32,10 @@ declare_clippy_lint! { impl_lint_pass!(AlmostCompleteLetterRange => [ALMOST_COMPLETE_LETTER_RANGE]); pub struct AlmostCompleteLetterRange { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl AlmostCompleteLetterRange { - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -46,7 +45,7 @@ impl EarlyLintPass for AlmostCompleteLetterRange { let ctxt = e.span.ctxt(); let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt) && let Some(end) = walk_span_to_context(end.span, ctxt) - && meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) + && self.msrv.meets(msrvs::RANGE_INCLUSIVE) { Some((trim_span(cx.sess().source_map(), start.between(end)), "..=")) } else { @@ -60,7 +59,7 @@ impl EarlyLintPass for AlmostCompleteLetterRange { if let PatKind::Range(Some(start), Some(end), kind) = &p.kind && matches!(kind.node, RangeEnd::Excluded) { - let sugg = if meets_msrv(self.msrv, msrvs::RANGE_INCLUSIVE) { + let sugg = if self.msrv.meets(msrvs::RANGE_INCLUSIVE) { "..=" } else { "..." diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs index 724490fb495..ccf82f132f4 100644 --- a/clippy_lints/src/approx_const.rs +++ b/clippy_lints/src/approx_const.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_help; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::msrvs::{self, Msrv}; use rustc_ast::ast::{FloatTy, LitFloatType, LitKind}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -63,12 +63,12 @@ const KNOWN_CONSTS: [(f64, &str, usize, Option<RustcVersion>); 19] = [ ]; pub struct ApproxConstant { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl ApproxConstant { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } @@ -87,7 +87,7 @@ impl ApproxConstant { let s = s.as_str(); if s.parse::<f64>().is_ok() { for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS { - if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| meets_msrv(self.msrv, msrv)) { + if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| self.msrv.meets(msrv)) { span_lint_and_help( cx, APPROX_CONSTANT, diff --git a/clippy_lints/src/attrs.rs b/clippy_lints/src/attrs.rs index 018f10f2588..0710ac0bb0a 100644 --- a/clippy_lints/src/attrs.rs +++ b/clippy_lints/src/attrs.rs @@ -2,9 +2,8 @@ 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::msrvs::{self, Msrv}; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; -use clippy_utils::{extract_msrv_attr, meets_msrv}; use if_chain::if_chain; use rustc_ast::{AttrKind, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem}; use rustc_errors::Applicability; @@ -14,7 +13,6 @@ use rustc_hir::{ use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::Symbol; @@ -599,7 +597,7 @@ fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool { } pub struct EarlyAttributes { - pub msrv: Option<RustcVersion>, + pub msrv: Msrv, } impl_lint_pass!(EarlyAttributes => [ @@ -614,7 +612,7 @@ impl EarlyLintPass for EarlyAttributes { } fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { - check_deprecated_cfg_attr(cx, attr, self.msrv); + check_deprecated_cfg_attr(cx, attr, &self.msrv); check_mismatched_target_os(cx, attr); } @@ -654,9 +652,9 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It } } -fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: Option<RustcVersion>) { +fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) { if_chain! { - if meets_msrv(msrv, msrvs::TOOL_ATTRIBUTES); + if msrv.meets(msrvs::TOOL_ATTRIBUTES); // check cfg_attr if attr.has_name(sym::cfg_attr); if let Some(items) = attr.meta_item_list(); diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs index 3f1edabe6c5..44226298333 100644 --- a/clippy_lints/src/casts/cast_abs_to_unsigned.rs +++ b/clippy_lints/src/casts/cast_abs_to_unsigned.rs @@ -1,11 +1,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; -use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_semver::RustcVersion; use super::CAST_ABS_TO_UNSIGNED; @@ -15,9 +14,9 @@ pub(super) fn check( cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: Option<RustcVersion>, + msrv: &Msrv, ) { - if meets_msrv(msrv, msrvs::UNSIGNED_ABS) + if msrv.meets(msrvs::UNSIGNED_ABS) && let ty::Int(from) = cast_from.kind() && let ty::Uint(to) = cast_to.kind() && let ExprKind::MethodCall(method_path, receiver, ..) = cast_expr.kind diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs index 13c403234da..cf07e050ccc 100644 --- a/clippy_lints/src/casts/cast_lossless.rs +++ b/clippy_lints/src/casts/cast_lossless.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::in_constant; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::is_isize_or_usize; -use clippy_utils::{in_constant, meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, FloatTy, Ty}; -use rustc_semver::RustcVersion; use super::{utils, CAST_LOSSLESS}; @@ -16,7 +16,7 @@ pub(super) fn check( cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, - msrv: Option<RustcVersion>, + msrv: &Msrv, ) { if !should_lint(cx, expr, cast_from, cast_to, msrv) { return; @@ -57,13 +57,7 @@ pub(super) fn check( ); } -fn should_lint( - cx: &LateContext<'_>, - expr: &Expr<'_>, - cast_from: Ty<'_>, - cast_to: Ty<'_>, - msrv: Option<RustcVersion>, -) -> bool { +fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool { // Do not suggest using From in consts/statics until it is valid to do so (see #2267). if in_constant(cx, expr.hir_id) { return false; @@ -89,7 +83,7 @@ fn should_lint( }; !is_isize_or_usize(cast_from) && from_nbits < to_nbits }, - (false, true) if matches!(cast_from.kind(), ty::Bool) && meets_msrv(msrv, msrvs::FROM_BOOL) => true, + (false, true) if matches!(cast_from.kind(), ty::Bool) && msrv.meets(msrvs::FROM_BOOL) => true, (_, _) => { matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64)) }, diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs index adbcfd3189b..a6376484914 100644 --- a/clippy_lints/src/casts/cast_possible_truncation.rs +++ b/clippy_lints/src/casts/cast_possible_truncation.rs @@ -118,12 +118,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, }; let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx); - let cast_from_ptr_size = def.repr().int.map_or(true, |ty| { - matches!( - ty, - IntegerType::Pointer(_), - ) - }); + let cast_from_ptr_size = def.repr().int.map_or(true, |ty| matches!(ty, IntegerType::Pointer(_),)); let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) { (false, false) if from_nbits > to_nbits => "", (true, false) if from_nbits > to_nbits => "", diff --git a/clippy_lints/src/casts/cast_slice_different_sizes.rs b/clippy_lints/src/casts/cast_slice_different_sizes.rs index d31d10d22b9..e862f13e69f 100644 --- a/clippy_lints/src/casts/cast_slice_different_sizes.rs +++ b/clippy_lints/src/casts/cast_slice_different_sizes.rs @@ -1,16 +1,16 @@ -use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::{diagnostics::span_lint_and_then, source}; use if_chain::if_chain; use rustc_ast::Mutability; use rustc_hir::{Expr, ExprKind, Node}; use rustc_lint::LateContext; use rustc_middle::ty::{self, layout::LayoutOf, Ty, TypeAndMut}; -use rustc_semver::RustcVersion; use super::CAST_SLICE_DIFFERENT_SIZES; -pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Option<RustcVersion>) { +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv) { // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist - if !meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS) { + if !msrv.meets(msrvs::PTR_SLICE_RAW_PARTS) { return; } diff --git a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs index 284ef165998..627b795d6ed 100644 --- a/clippy_lints/src/casts/cast_slice_from_raw_parts.rs +++ b/clippy_lints/src/casts/cast_slice_from_raw_parts.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{match_def_path, meets_msrv, msrvs, paths}; +use clippy_utils::{match_def_path, paths}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, Expr, ExprKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty}; -use rustc_semver::RustcVersion; use super::CAST_SLICE_FROM_RAW_PARTS; @@ -25,15 +25,9 @@ fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> { } } -pub(super) fn check( - cx: &LateContext<'_>, - expr: &Expr<'_>, - cast_expr: &Expr<'_>, - cast_to: Ty<'_>, - msrv: Option<RustcVersion>, -) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) { if_chain! { - if meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS); + if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS); if let ty::RawPtr(ptrty) = cast_to.kind(); if let ty::Slice(_) = ptrty.ty.kind(); if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind; diff --git a/clippy_lints/src/casts/mod.rs b/clippy_lints/src/casts/mod.rs index 7148b5e6ebf..c6d505c4a18 100644 --- a/clippy_lints/src/casts/mod.rs +++ b/clippy_lints/src/casts/mod.rs @@ -21,11 +21,11 @@ mod ptr_as_ptr; mod unnecessary_cast; mod utils; -use clippy_utils::{is_hir_ty_cfg_dependant, meets_msrv, msrvs}; +use clippy_utils::is_hir_ty_cfg_dependant; +use clippy_utils::msrvs::{self, Msrv}; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -648,12 +648,12 @@ declare_clippy_lint! { } pub struct Casts { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl Casts { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -686,7 +686,7 @@ impl_lint_pass!(Casts => [ impl<'tcx> LateLintPass<'tcx> for Casts { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !in_external_macro(cx.sess(), expr.span) { - ptr_as_ptr::check(cx, expr, self.msrv); + ptr_as_ptr::check(cx, expr, &self.msrv); } if expr.span.from_expansion() { @@ -705,7 +705,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts { if unnecessary_cast::check(cx, expr, cast_expr, cast_from, cast_to) { return; } - cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, self.msrv); + cast_slice_from_raw_parts::check(cx, expr, cast_expr, cast_to, &self.msrv); as_ptr_cast_mut::check(cx, expr, cast_expr, cast_to); fn_to_numeric_cast_any::check(cx, expr, cast_expr, cast_from, cast_to); fn_to_numeric_cast::check(cx, expr, cast_expr, cast_from, cast_to); @@ -717,16 +717,16 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_possible_wrap::check(cx, expr, cast_from, cast_to); cast_precision_loss::check(cx, expr, cast_from, cast_to); cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to); - cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); + cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); cast_nan_to_int::check(cx, expr, cast_expr, cast_from, cast_to); } - cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, self.msrv); + cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv); cast_enum_constructor::check(cx, expr, cast_expr, cast_from); } as_underscore::check(cx, expr, cast_to_hir); - if meets_msrv(self.msrv, msrvs::BORROW_AS_PTR) { + if self.msrv.meets(msrvs::BORROW_AS_PTR) { borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir); } } @@ -734,8 +734,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts { cast_ref_to_mut::check(cx, expr); cast_ptr_alignment::check(cx, expr); char_lit_as_u8::check(cx, expr); - ptr_as_ptr::check(cx, expr, self.msrv); - cast_slice_different_sizes::check(cx, expr, self.msrv); + ptr_as_ptr::check(cx, expr, &self.msrv); + cast_slice_different_sizes::check(cx, expr, &self.msrv); } extract_msrv_attr!(LateContext); diff --git a/clippy_lints/src/casts/ptr_as_ptr.rs b/clippy_lints/src/casts/ptr_as_ptr.rs index b9509ca656f..15ffb00da88 100644 --- a/clippy_lints/src/casts/ptr_as_ptr.rs +++ b/clippy_lints/src/casts/ptr_as_ptr.rs @@ -1,19 +1,18 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::sugg::Sugg; -use clippy_utils::{meets_msrv, msrvs}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Mutability, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, TypeAndMut}; -use rustc_semver::RustcVersion; use super::PTR_AS_PTR; -pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Option<RustcVersion>) { - if !meets_msrv(msrv, msrvs::POINTER_CAST) { +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) { + if !msrv.meets(msrvs::POINTER_CAST) { return; } diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs index c8596987e4d..7e23318076c 100644 --- a/clippy_lints/src/casts/unnecessary_cast.rs +++ b/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::get_parent_expr; use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::source::snippet_opt; +use clippy_utils::{get_parent_expr, path_to_local}; use if_chain::if_chain; use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_errors::Applicability; @@ -75,13 +75,26 @@ pub(super) fn check<'tcx>( } if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) { + if let Some(id) = path_to_local(cast_expr) + && let Some(span) = cx.tcx.hir().opt_span(id) + && span.ctxt() != cast_expr.span.ctxt() + { + // Binding context is different than the identifiers context. + // Weird macro wizardry could be involved here. + return false; + } + span_lint_and_sugg( cx, UNNECESSARY_CAST, expr.span, &format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"), "try", - cast_str, + if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(..))) { + format!("{{ {cast_str} }}") + } else { + cast_str + }, Applicability::MachineApplicable, ); return true; diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs index 78e9921f036..9102a89e377 100644 --- a/clippy_lints/src/checked_conversions.rs +++ b/clippy_lints/src/checked_conversions.rs @@ -1,14 +1,14 @@ //! lint on manually implemented checked conversions that could be transformed into `try_from` use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{in_constant, is_integer_literal, meets_msrv, msrvs, SpanlessEq}; +use clippy_utils::{in_constant, is_integer_literal, SpanlessEq}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -37,12 +37,12 @@ declare_clippy_lint! { } pub struct CheckedConversions { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl CheckedConversions { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -51,7 +51,7 @@ impl_lint_pass!(CheckedConversions => [CHECKED_CONVERSIONS]); impl<'tcx> LateLintPass<'tcx> for CheckedConversions { fn check_expr(&mut self, cx: &LateContext<'_>, item: &Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::TRY_FROM) { + if !self.msrv.meets(msrvs::TRY_FROM) { return; } diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs index 90430b71a0e..b38e09dc09f 100644 --- a/clippy_lints/src/collapsible_if.rs +++ b/clippy_lints/src/collapsible_if.rs @@ -160,11 +160,13 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind; // Prevent triggering on `if c { if let a = b { .. } }`. if !matches!(check_inner.kind, ast::ExprKind::Let(..)); - if expr.span.ctxt() == inner.span.ctxt(); + let ctxt = expr.span.ctxt(); + if inner.span.ctxt() == ctxt; then { span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| { - let lhs = Sugg::ast(cx, check, ".."); - let rhs = Sugg::ast(cx, check_inner, ".."); + let mut app = Applicability::MachineApplicable; + let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app); + let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app); diag.span_suggestion( expr.span, "collapse nested if block", @@ -173,7 +175,7 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: & lhs.and(&rhs), snippet_block(cx, content.span, "..", Some(expr.span)), ), - Applicability::MachineApplicable, // snippet + app, // snippet ); }); } diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 0d3fc43a644..e4d76f07d6b 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -177,6 +177,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO, crate::from_str_radix_10::FROM_STR_RADIX_10_INFO, crate::functions::DOUBLE_MUST_USE_INFO, + crate::functions::MISNAMED_GETTERS_INFO, crate::functions::MUST_USE_CANDIDATE_INFO, crate::functions::MUST_USE_UNIT_INFO, crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO, @@ -583,6 +584,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[ crate::types::TYPE_COMPLEXITY_INFO, crate::types::VEC_BOX_INFO, crate::undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS_INFO, + crate::undocumented_unsafe_blocks::UNNECESSARY_SAFETY_COMMENT_INFO, crate::unicode::INVISIBLE_CHARACTERS_INFO, crate::unicode::NON_ASCII_LITERAL_INFO, crate::unicode::UNICODE_NOT_NFC_INFO, diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs index 47ea98956be..38329659e02 100644 --- a/clippy_lints/src/dereference.rs +++ b/clippy_lints/src/dereference.rs @@ -1,12 +1,13 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_with_applicability, snippet_with_context}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::ty::{expr_sig, is_copy, peel_mid_ty_refs, ty_sig, variant_of_res}; use clippy_utils::{ - fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, meets_msrv, msrvs, path_to_local, - walk_to_expr_usage, + fn_def_id, get_parent_expr, get_parent_expr_for_hir, is_lint_allowed, path_to_local, walk_to_expr_usage, }; + use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::graph::iterate::{CycleDetector, TriColorDepthFirstSearch}; @@ -28,7 +29,6 @@ use rustc_middle::ty::{ self, Binder, BoundVariableKind, Clause, EarlyBinder, FnSig, GenericArgKind, List, ParamTy, PredicateKind, ProjectionPredicate, Ty, TyCtxt, TypeVisitable, TypeckResults, }; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::sym, Span, Symbol}; use rustc_trait_selection::infer::InferCtxtExt as _; @@ -181,12 +181,12 @@ pub struct Dereferencing<'tcx> { possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, // `IntoIterator` for arrays requires Rust 1.53. - msrv: Option<RustcVersion>, + msrv: Msrv, } impl<'tcx> Dereferencing<'tcx> { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv, ..Dereferencing::default() @@ -286,26 +286,27 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { match (self.state.take(), kind) { (None, kind) => { let expr_ty = typeck.expr_ty(expr); - let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, self.msrv); + let (position, adjustments) = walk_parents(cx, &mut self.possible_borrowers, expr, &self.msrv); match kind { RefOp::Deref => { + let sub_ty = typeck.expr_ty(sub_expr); if let Position::FieldAccess { name, of_union: false, } = position - && !ty_contains_field(typeck.expr_ty(sub_expr), name) + && !ty_contains_field(sub_ty, name) { self.state = Some(( State::ExplicitDerefField { name }, StateData { span: expr.span, hir_id: expr.hir_id, position }, )); - } else if position.is_deref_stable() { + } else if position.is_deref_stable() && sub_ty.is_ref() { self.state = Some(( State::ExplicitDeref { mutability: None }, StateData { span: expr.span, hir_id: expr.hir_id, position }, )); } - } + }, RefOp::Method(target_mut) if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id) && position.lint_explicit_deref() => @@ -320,7 +321,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { StateData { span: expr.span, hir_id: expr.hir_id, - position + position, }, )); }, @@ -394,7 +395,11 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { msg, snip_expr, }), - StateData { span: expr.span, hir_id: expr.hir_id, position }, + StateData { + span: expr.span, + hir_id: expr.hir_id, + position, + }, )); } else if position.is_deref_stable() // Auto-deref doesn't combine with other adjustments @@ -406,7 +411,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> { StateData { span: expr.span, hir_id: expr.hir_id, - position + position, }, )); } @@ -698,7 +703,7 @@ fn walk_parents<'tcx>( cx: &LateContext<'tcx>, possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>, e: &'tcx Expr<'_>, - msrv: Option<RustcVersion>, + msrv: &Msrv, ) -> (Position, &'tcx [Adjustment<'tcx>]) { let mut adjustments = [].as_slice(); let mut precedence = 0i8; @@ -862,7 +867,11 @@ fn walk_parents<'tcx>( } && impl_ty.is_ref() && let infcx = cx.tcx.infer_ctxt().build() && infcx - .type_implements_trait(trait_id, [impl_ty.into()].into_iter().chain(subs.iter().copied()), cx.param_env) + .type_implements_trait( + trait_id, + [impl_ty.into()].into_iter().chain(subs.iter().copied()), + cx.param_env, + ) .must_apply_modulo_regions() { return Some(Position::MethodReceiverRefImpl) @@ -1078,7 +1087,7 @@ fn needless_borrow_impl_arg_position<'tcx>( param_ty: ParamTy, mut expr: &Expr<'tcx>, precedence: i8, - msrv: Option<RustcVersion>, + msrv: &Msrv, ) -> Position { let destruct_trait_def_id = cx.tcx.lang_items().destruct_trait(); let sized_trait_def_id = cx.tcx.lang_items().sized_trait(); @@ -1178,7 +1187,7 @@ fn needless_borrow_impl_arg_position<'tcx>( && let ty::Param(param_ty) = trait_predicate.self_ty().kind() && let GenericArgKind::Type(ty) = substs_with_referent_ty[param_ty.index as usize].unpack() && ty.is_array() - && !meets_msrv(msrv, msrvs::ARRAY_INTO_ITERATOR) + && !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) { return false; } diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs index d870e0ceef4..9e596ca8157 100644 --- a/clippy_lints/src/derive.rs +++ b/clippy_lints/src/derive.rs @@ -14,8 +14,8 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::traits::Reveal; use rustc_middle::ty::{ - self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, TraitRef, - Ty, TyCtxt, + self, Binder, BoundConstness, Clause, GenericParamDefKind, ImplPolarity, ParamEnv, PredicateKind, TraitPredicate, + TraitRef, Ty, TyCtxt, }; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; diff --git a/clippy_lints/src/doc.rs b/clippy_lints/src/doc.rs index ae5f9424b23..cdc23a4d227 100644 --- a/clippy_lints/src/doc.rs +++ b/clippy_lints/src/doc.rs @@ -253,7 +253,7 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.66.0"] pub UNNECESSARY_SAFETY_DOC, - style, + restriction, "`pub fn` or `pub trait` with `# Safety` docs" } diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs index f34cbee0355..3543910c3b5 100644 --- a/clippy_lints/src/eta_reduction.rs +++ b/clippy_lints/src/eta_reduction.rs @@ -11,7 +11,7 @@ use rustc_hir::{Closure, Expr, ExprKind, Param, PatKind, Unsafety}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::binding::BindingMode; -use rustc_middle::ty::{self, Ty, TypeVisitable}; +use rustc_middle::ty::{self, EarlyBinder, SubstsRef, Ty, TypeVisitable}; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::symbol::sym; @@ -125,7 +125,12 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if let Some(mut snippet) = snippet_opt(cx, callee.span) { if let Some(fn_mut_id) = cx.tcx.lang_items().fn_mut_trait() && let args = cx.tcx.erase_late_bound_regions(substs.as_closure().sig()).inputs() - && implements_trait(cx, callee_ty.peel_refs(), fn_mut_id, &args.iter().copied().map(Into::into).collect::<Vec<_>>()) + && implements_trait( + cx, + callee_ty.peel_refs(), + fn_mut_id, + &args.iter().copied().map(Into::into).collect::<Vec<_>>(), + ) && path_to_local(callee).map_or(false, |l| local_used_after_expr(cx, l, expr)) { // Mutable closure is used after current expr; we cannot consume it. @@ -152,7 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction { if check_sig(cx, closure_ty, call_ty); then { span_lint_and_then(cx, REDUNDANT_CLOSURE_FOR_METHOD_CALLS, expr.span, "redundant closure", |diag| { - let name = get_ufcs_type_name(cx, method_def_id); + let name = get_ufcs_type_name(cx, method_def_id, substs); diag.span_suggestion( expr.span, "replace the closure with the method itself", @@ -222,7 +227,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure_ty: Ty<'tcx>, call_ty: Ty<'tc cx.tcx.erase_late_bound_regions(closure_sig) == cx.tcx.erase_late_bound_regions(call_sig) } -fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String { +fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, substs: SubstsRef<'tcx>) -> String { let assoc_item = cx.tcx.associated_item(method_def_id); let def_id = assoc_item.container_id(cx.tcx); match assoc_item.container { @@ -231,6 +236,15 @@ fn get_ufcs_type_name(cx: &LateContext<'_>, method_def_id: DefId) -> String { let ty = cx.tcx.type_of(def_id); match ty.kind() { ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did()), + ty::Array(..) + | ty::Dynamic(..) + | ty::Never + | ty::RawPtr(_) + | ty::Ref(..) + | ty::Slice(_) + | ty::Tuple(_) => { + format!("<{}>", EarlyBinder(ty).subst(cx.tcx, substs)) + }, _ => ty.to_string(), } }, diff --git a/clippy_lints/src/exit.rs b/clippy_lints/src/exit.rs index 407dd1b3957..9c8b0d076df 100644 --- a/clippy_lints/src/exit.rs +++ b/clippy_lints/src/exit.rs @@ -7,21 +7,34 @@ use rustc_session::{declare_lint_pass, declare_tool_lint}; declare_clippy_lint! { /// ### What it does - /// `exit()` terminates the program and doesn't provide a - /// stack trace. + /// Detects calls to the `exit()` function which terminates the program. /// /// ### Why is this bad? - /// Ideally a program is terminated by finishing + /// Exit terminates the program at the location it is called. For unrecoverable + /// errors `panics` should be used to provide a stacktrace and potentualy other + /// information. A normal termination or one with an error code should happen in /// the main function. /// /// ### Example - /// ```ignore + /// ``` /// std::process::exit(0) /// ``` + /// + /// Use instead: + /// + /// ```ignore + /// // To provide a stacktrace and additional information + /// panic!("message"); + /// + /// // or a main method with a return + /// fn main() -> Result<(), i32> { + /// Ok(()) + /// } + /// ``` #[clippy::version = "1.41.0"] pub EXIT, restriction, - "`std::process::exit` is called, terminating the program" + "detects `std::process::exit` calls" } declare_lint_pass!(Exit => [EXIT]); diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs index f0fe845d330..111b624f529 100644 --- a/clippy_lints/src/format_args.rs +++ b/clippy_lints/src/format_args.rs @@ -1,19 +1,22 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; -use clippy_utils::macros::FormatParamKind::{Implicit, Named, Numbered, Starred}; +use clippy_utils::is_diag_trait_item; +use clippy_utils::macros::FormatParamKind::{Implicit, Named, NamedInline, Numbered, Starred}; use clippy_utils::macros::{ is_format_macro, is_panic, root_macro_call, Count, FormatArg, FormatArgsExpn, FormatParam, FormatParamUsage, }; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; -use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs}; use if_chain::if_chain; use itertools::Itertools; -use rustc_errors::Applicability; +use rustc_errors::{ + Applicability, + SuggestionStyle::{CompletelyHidden, ShowCode}, +}; use rustc_hir::{Expr, ExprKind, HirId, QPath}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::ty::adjustment::{Adjust, Adjustment}; use rustc_middle::ty::Ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::DefId; use rustc_span::edition::Edition::Edition2021; @@ -103,19 +106,25 @@ declare_clippy_lint! { /// format!("{var:.prec$}"); /// ``` /// - /// ### Known Problems - /// - /// There may be a false positive if the format string is expanded from certain proc macros: - /// - /// ```ignore - /// println!(indoc!("{}"), var); + /// If allow-mixed-uninlined-format-args is set to false in clippy.toml, + /// the following code will also trigger the lint: + /// ```rust + /// # let var = 42; + /// format!("{} {}", var, 1+2); + /// ``` + /// Use instead: + /// ```rust + /// # let var = 42; + /// format!("{var} {}", 1+2); /// ``` /// + /// ### Known Problems + /// /// If a format string contains a numbered argument that cannot be inlined /// nothing will be suggested, e.g. `println!("{0}={1}", var, 1+2)`. #[clippy::version = "1.65.0"] pub UNINLINED_FORMAT_ARGS, - pedantic, + style, "using non-inlined variables in `format!` calls" } @@ -158,13 +167,17 @@ impl_lint_pass!(FormatArgs => [ ]); pub struct FormatArgs { - msrv: Option<RustcVersion>, + msrv: Msrv, + ignore_mixed: bool, } impl FormatArgs { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { - Self { msrv } + pub fn new(msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self { + Self { + msrv, + ignore_mixed: allow_mixed_uninlined_format_args, + } } } @@ -188,8 +201,8 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs { check_format_in_format_args(cx, outermost_expn_data.call_site, name, arg.param.value); check_to_string_in_format_args(cx, name, arg.param.value); } - if meets_msrv(self.msrv, msrvs::FORMAT_ARGS_CAPTURE) { - check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id); + if self.msrv.meets(msrvs::FORMAT_ARGS_CAPTURE) { + check_uninlined_args(cx, &format_args, outermost_expn_data.call_site, macro_def_id, self.ignore_mixed); } } } @@ -267,7 +280,13 @@ fn check_unused_format_specifier(cx: &LateContext<'_>, arg: &FormatArg<'_>) { } } -fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_site: Span, def_id: DefId) { +fn check_uninlined_args( + cx: &LateContext<'_>, + args: &FormatArgsExpn<'_>, + call_site: Span, + def_id: DefId, + ignore_mixed: bool, +) { if args.format_string.span.from_expansion() { return; } @@ -282,14 +301,13 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si // we cannot remove any other arguments in the format string, // because the index numbers might be wrong after inlining. // Example of an un-inlinable format: print!("{}{1}", foo, 2) - if !args.params().all(|p| check_one_arg(args, &p, &mut fixes)) || fixes.is_empty() { + if !args.params().all(|p| check_one_arg(args, &p, &mut fixes, ignore_mixed)) || fixes.is_empty() { return; } - // Temporarily ignore multiline spans: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308 - if fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)) { - return; - } + // multiline span display suggestion is sometimes broken: https://github.com/rust-lang/rust/pull/102729#discussion_r988704308 + // in those cases, make the code suggestion hidden + let multiline_fix = fixes.iter().any(|(span, _)| cx.sess().source_map().is_multiline(*span)); span_lint_and_then( cx, @@ -297,12 +315,22 @@ fn check_uninlined_args(cx: &LateContext<'_>, args: &FormatArgsExpn<'_>, call_si call_site, "variables can be used directly in the `format!` string", |diag| { - diag.multipart_suggestion("change this to", fixes, Applicability::MachineApplicable); + diag.multipart_suggestion_with_style( + "change this to", + fixes, + Applicability::MachineApplicable, + if multiline_fix { CompletelyHidden } else { ShowCode }, + ); }, ); } -fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut Vec<(Span, String)>) -> bool { +fn check_one_arg( + args: &FormatArgsExpn<'_>, + param: &FormatParam<'_>, + fixes: &mut Vec<(Span, String)>, + ignore_mixed: bool, +) -> bool { if matches!(param.kind, Implicit | Starred | Named(_) | Numbered) && let ExprKind::Path(QPath::Resolved(None, path)) = param.value.kind && let [segment] = path.segments @@ -317,8 +345,10 @@ fn check_one_arg(args: &FormatArgsExpn<'_>, param: &FormatParam<'_>, fixes: &mut fixes.push((arg_span, String::new())); true // successful inlining, continue checking } else { - // if we can't inline a numbered argument, we can't continue - param.kind != Numbered + // Do not continue inlining (return false) in case + // * if we can't inline a numbered argument, e.g. `print!("{0} ...", foo.bar, ...)` + // * if allow_mixed_uninlined_format_args is false and this arg hasn't been inlined already + param.kind != Numbered && (!ignore_mixed || matches!(param.kind, NamedInline(_))) } } diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs index 8b24a4962fb..8621504c1b4 100644 --- a/clippy_lints/src/from_over_into.rs +++ b/clippy_lints/src/from_over_into.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::path_def_id; use clippy_utils::source::snippet_opt; -use clippy_utils::{meets_msrv, msrvs, path_def_id}; use rustc_errors::Applicability; use rustc_hir::intravisit::{walk_path, Visitor}; use rustc_hir::{ @@ -10,7 +11,6 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter::OnlyBodies; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::{kw, sym}; use rustc_span::{Span, Symbol}; @@ -49,12 +49,12 @@ declare_clippy_lint! { } pub struct FromOverInto { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl FromOverInto { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { FromOverInto { msrv } } } @@ -63,7 +63,7 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]); impl<'tcx> LateLintPass<'tcx> for FromOverInto { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { - if !meets_msrv(self.msrv, msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) { + if !self.msrv.meets(msrvs::RE_REBALANCING_COHERENCE) || !span_is_local(item.span) { return; } diff --git a/clippy_lints/src/functions/misnamed_getters.rs b/clippy_lints/src/functions/misnamed_getters.rs new file mode 100644 index 00000000000..27acad45ccf --- /dev/null +++ b/clippy_lints/src/functions/misnamed_getters.rs @@ -0,0 +1,125 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet; +use rustc_errors::Applicability; +use rustc_hir::{intravisit::FnKind, Body, ExprKind, FnDecl, HirId, ImplicitSelfKind, Unsafety}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::Span; + +use std::iter; + +use super::MISNAMED_GETTERS; + +pub fn check_fn( + cx: &LateContext<'_>, + kind: FnKind<'_>, + decl: &FnDecl<'_>, + body: &Body<'_>, + span: Span, + _hir_id: HirId, +) { + let FnKind::Method(ref ident, sig) = kind else { + return; + }; + + // Takes only &(mut) self + if decl.inputs.len() != 1 { + return; + } + + let name = ident.name.as_str(); + + let name = match decl.implicit_self { + ImplicitSelfKind::MutRef => { + let Some(name) = name.strip_suffix("_mut") else { + return; + }; + name + }, + ImplicitSelfKind::Imm | ImplicitSelfKind::Mut | ImplicitSelfKind::ImmRef => name, + ImplicitSelfKind::None => return, + }; + + let name = if sig.header.unsafety == Unsafety::Unsafe { + name.strip_suffix("_unchecked").unwrap_or(name) + } else { + name + }; + + // Body must be &(mut) <self_data>.name + // self_data is not neccessarilly self, to also lint sub-getters, etc… + + let block_expr = if_chain! { + if let ExprKind::Block(block,_) = body.value.kind; + if block.stmts.is_empty(); + if let Some(block_expr) = block.expr; + then { + block_expr + } else { + return; + } + }; + let expr_span = block_expr.span; + + // Accept &<expr>, &mut <expr> and <expr> + let expr = if let ExprKind::AddrOf(_, _, tmp) = block_expr.kind { + tmp + } else { + block_expr + }; + let (self_data, used_ident) = if_chain! { + if let ExprKind::Field(self_data, ident) = expr.kind; + if ident.name.as_str() != name; + then { + (self_data, ident) + } else { + return; + } + }; + + let mut used_field = None; + let mut correct_field = None; + let typeck_results = cx.typeck_results(); + for adjusted_type in iter::once(typeck_results.expr_ty(self_data)) + .chain(typeck_results.expr_adjustments(self_data).iter().map(|adj| adj.target)) + { + let ty::Adt(def,_) = adjusted_type.kind() else { + continue; + }; + + for f in def.all_fields() { + if f.name.as_str() == name { + correct_field = Some(f); + } + if f.name == used_ident.name { + used_field = Some(f); + } + } + } + + let Some(used_field) = used_field else { + // Can happen if the field access is a tuple. We don't lint those because the getter name could not start with a number. + return; + }; + + let Some(correct_field) = correct_field else { + // There is no field corresponding to the getter name. + // FIXME: This can be a false positive if the correct field is reachable trought deeper autodereferences than used_field is + return; + }; + + if cx.tcx.type_of(used_field.did) == cx.tcx.type_of(correct_field.did) { + let left_span = block_expr.span.until(used_ident.span); + let snippet = snippet(cx, left_span, ".."); + let sugg = format!("{snippet}{name}"); + span_lint_and_then( + cx, + MISNAMED_GETTERS, + span, + "getter function appears to return the wrong field", + |diag| { + diag.span_suggestion(expr_span, "consider using", sugg, Applicability::MaybeIncorrect); + }, + ); + } +} diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index ae0e0833446..91e6ffe6447 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -1,3 +1,4 @@ +mod misnamed_getters; mod must_use; mod not_unsafe_ptr_arg_deref; mod result; @@ -260,6 +261,48 @@ declare_clippy_lint! { "function returning `Result` with large `Err` type" } +declare_clippy_lint! { + /// ### What it does + /// Checks for getter methods that return a field that doesn't correspond + /// to the name of the method, when there is a field's whose name matches that of the method. + /// + /// ### Why is this bad? + /// It is most likely that such a method is a bug caused by a typo or by copy-pasting. + /// + /// ### Example + + /// ```rust + /// struct A { + /// a: String, + /// b: String, + /// } + /// + /// impl A { + /// fn a(&self) -> &str{ + /// &self.b + /// } + /// } + + /// ``` + /// Use instead: + /// ```rust + /// struct A { + /// a: String, + /// b: String, + /// } + /// + /// impl A { + /// fn a(&self) -> &str{ + /// &self.a + /// } + /// } + /// ``` + #[clippy::version = "1.67.0"] + pub MISNAMED_GETTERS, + suspicious, + "getter method returning the wrong field" +} + #[derive(Copy, Clone)] pub struct Functions { too_many_arguments_threshold: u64, @@ -286,6 +329,7 @@ impl_lint_pass!(Functions => [ MUST_USE_CANDIDATE, RESULT_UNIT_ERR, RESULT_LARGE_ERR, + MISNAMED_GETTERS, ]); impl<'tcx> LateLintPass<'tcx> for Functions { @@ -301,6 +345,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions { too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold); too_many_lines::check_fn(cx, kind, span, body, self.too_many_lines_threshold); not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, hir_id); + misnamed_getters::check_fn(cx, kind, decl, body, span, hir_id); } fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { diff --git a/clippy_lints/src/functions/result.rs b/clippy_lints/src/functions/result.rs index f7e30b051a6..23da145d038 100644 --- a/clippy_lints/src/functions/result.rs +++ b/clippy_lints/src/functions/result.rs @@ -94,7 +94,9 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty if let hir::ItemKind::Enum(ref def, _) = item.kind; then { let variants_size = AdtVariantInfo::new(cx, *adt, subst); - if variants_size[0].size >= large_err_threshold { + if let Some((first_variant, variants)) = variants_size.split_first() + && first_variant.size >= large_err_threshold + { span_lint_and_then( cx, RESULT_LARGE_ERR, @@ -102,11 +104,11 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty "the `Err`-variant returned from this function is very large", |diag| { diag.span_label( - def.variants[variants_size[0].ind].span, + def.variants[first_variant.ind].span, format!("the largest variant contains at least {} bytes", variants_size[0].size), ); - for variant in &variants_size[1..] { + for variant in variants { if variant.size >= large_err_threshold { let variant_def = &def.variants[variant.ind]; diag.span_label( diff --git a/clippy_lints/src/future_not_send.rs b/clippy_lints/src/future_not_send.rs index a9425a40f88..61934a91426 100644 --- a/clippy_lints/src/future_not_send.rs +++ b/clippy_lints/src/future_not_send.rs @@ -91,7 +91,9 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend { infcx .err_ctxt() .maybe_note_obligation_cause_for_async_await(db, &obligation); - if let PredicateKind::Clause(Clause::Trait(trait_pred)) = obligation.predicate.kind().skip_binder() { + if let PredicateKind::Clause(Clause::Trait(trait_pred)) = + obligation.predicate.kind().skip_binder() + { db.note(&format!( "`{}` doesn't implement `{}`", trait_pred.self_ty(), diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs index 0d6718c168a..9cadaaa493e 100644 --- a/clippy_lints/src/if_then_some_else_none.rs +++ b/clippy_lints/src/if_then_some_else_none.rs @@ -1,14 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::eager_or_lazy::switch_to_eager_eval; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_macro_callsite; -use clippy_utils::{ - contains_return, higher, is_else_clause, is_res_lang_ctor, meets_msrv, msrvs, path_res, peel_blocks, -}; +use clippy_utils::{contains_return, higher, is_else_clause, is_res_lang_ctor, path_res, peel_blocks}; use rustc_hir::LangItem::{OptionNone, OptionSome}; use rustc_hir::{Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -47,12 +45,12 @@ declare_clippy_lint! { } pub struct IfThenSomeElseNone { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl IfThenSomeElseNone { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -61,7 +59,7 @@ impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]); impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !meets_msrv(self.msrv, msrvs::BOOL_THEN) { + if !self.msrv.meets(msrvs::BOOL_THEN) { return; } @@ -94,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone { } else { format!("{{ /* snippet */ {arg_snip} }}") }; - let method_name = if switch_to_eager_eval(cx, expr) && meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) { + let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(msrvs::BOOL_THEN_SOME) { "then_some" } else { method_body.insert_str(0, "|| "); diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs index 0d5099bde6d..cf35b1f175c 100644 --- a/clippy_lints/src/index_refutable_slice.rs +++ b/clippy_lints/src/index_refutable_slice.rs @@ -1,8 +1,9 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLet; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::is_copy; -use clippy_utils::{is_expn_of, is_lint_allowed, meets_msrv, msrvs, path_to_local}; +use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local}; use if_chain::if_chain; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_errors::Applicability; @@ -11,7 +12,6 @@ use rustc_hir::intravisit::{self, Visitor}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::hir::nested_filter; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::Ident, Span}; @@ -47,18 +47,17 @@ declare_clippy_lint! { /// ``` #[clippy::version = "1.59.0"] pub INDEX_REFUTABLE_SLICE, - nursery, + pedantic, "avoid indexing on slices which could be destructed" } -#[derive(Copy, Clone)] pub struct IndexRefutableSlice { max_suggested_slice: u64, - msrv: Option<RustcVersion>, + msrv: Msrv, } impl IndexRefutableSlice { - pub fn new(max_suggested_slice_pattern_length: u64, msrv: Option<RustcVersion>) -> Self { + pub fn new(max_suggested_slice_pattern_length: u64, msrv: Msrv) -> Self { Self { max_suggested_slice: max_suggested_slice_pattern_length, msrv, @@ -74,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some(); if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr); if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id); - if meets_msrv(self.msrv, msrvs::SLICE_PATTERNS); + if self.msrv.meets(msrvs::SLICE_PATTERNS); let found_slices = find_slice_values(cx, let_pat); if !found_slices.is_empty(); diff --git a/clippy_lints/src/instant_subtraction.rs b/clippy_lints/src/instant_subtraction.rs index 60754b224fc..dd1b23e7d9d 100644 --- a/clippy_lints/src/instant_subtraction.rs +++ b/clippy_lints/src/instant_subtraction.rs @@ -1,13 +1,11 @@ -use clippy_utils::{ - diagnostics::{self, span_lint_and_sugg}, - meets_msrv, msrvs, source, - sugg::Sugg, - ty, -}; +use clippy_utils::diagnostics::{self, span_lint_and_sugg}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::source; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{source_map::Spanned, sym}; @@ -68,12 +66,12 @@ declare_clippy_lint! { } pub struct InstantSubtraction { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl InstantSubtraction { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -101,7 +99,7 @@ impl LateLintPass<'_> for InstantSubtraction { } else { if_chain! { if !expr.span.from_expansion(); - if meets_msrv(self.msrv, msrvs::TRY_FROM); + if self.msrv.meets(msrvs::TRY_FROM); if is_an_instant(cx, lhs); if is_a_duration(cx, rhs); diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 601990cd6a3..7b17d8a156d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -52,10 +52,9 @@ extern crate declare_clippy_lint; use std::io; use std::path::PathBuf; -use clippy_utils::parse_msrv; +use clippy_utils::msrvs::Msrv; use rustc_data_structures::fx::FxHashSet; use rustc_lint::{Lint, LintId}; -use rustc_semver::RustcVersion; use rustc_session::Session; #[cfg(feature = "internal")] @@ -322,48 +321,10 @@ pub use crate::utils::conf::{lookup_conf_file, Conf}; /// Used in `./src/driver.rs`. pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. + let msrv = Msrv::read(&conf.msrv, sess); + let msrv = move || msrv.clone(); - let msrv = conf.msrv.as_ref().and_then(|s| { - parse_msrv(s, None, None).or_else(|| { - sess.err(format!( - "error reading Clippy's configuration file. `{s}` is not a valid Rust version" - )); - None - }) - }); - - store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv })); -} - -fn read_msrv(conf: &Conf, sess: &Session) -> Option<RustcVersion> { - let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION") - .ok() - .and_then(|v| parse_msrv(&v, None, None)); - let clippy_msrv = conf.msrv.as_ref().and_then(|s| { - parse_msrv(s, None, None).or_else(|| { - sess.err(format!( - "error reading Clippy's configuration file. `{s}` is not a valid Rust version" - )); - None - }) - }); - - if let Some(cargo_msrv) = cargo_msrv { - if let Some(clippy_msrv) = clippy_msrv { - // if both files have an msrv, let's compare them and emit a warning if they differ - if clippy_msrv != cargo_msrv { - sess.warn(format!( - "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{clippy_msrv}` from `clippy.toml`" - )); - } - - Some(clippy_msrv) - } else { - Some(cargo_msrv) - } - } else { - clippy_msrv - } + store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv: msrv() })); } #[doc(hidden)] @@ -595,43 +556,44 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); - let msrv = read_msrv(conf, sess); + let msrv = Msrv::read(&conf.msrv, sess); + let msrv = move || msrv.clone(); let avoid_breaking_exported_api = conf.avoid_breaking_exported_api; let allow_expect_in_tests = conf.allow_expect_in_tests; let allow_unwrap_in_tests = conf.allow_unwrap_in_tests; - store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv))); + store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); store.register_late_pass(move |_| { Box::new(methods::Methods::new( avoid_breaking_exported_api, - msrv, + msrv(), allow_expect_in_tests, allow_unwrap_in_tests, )) }); - store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv))); + store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv()))); let matches_for_let_else = conf.matches_for_let_else; - store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv, matches_for_let_else))); - store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv))); - store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv))); - store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv))); - store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv))); - store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv))); - store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv))); - store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv))); - store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv))); - store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv))); - store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv))); - store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_let_else::ManualLetElse::new(msrv(), matches_for_let_else))); + store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv()))); + store.register_early_pass(move || Box::new(redundant_static_lifetimes::RedundantStaticLifetimes::new(msrv()))); + store.register_early_pass(move || Box::new(redundant_field_names::RedundantFieldNames::new(msrv()))); + store.register_late_pass(move |_| Box::new(checked_conversions::CheckedConversions::new(msrv()))); + store.register_late_pass(move |_| Box::new(mem_replace::MemReplace::new(msrv()))); + store.register_late_pass(move |_| Box::new(ranges::Ranges::new(msrv()))); + store.register_late_pass(move |_| Box::new(from_over_into::FromOverInto::new(msrv()))); + store.register_late_pass(move |_| Box::new(use_self::UseSelf::new(msrv()))); + store.register_late_pass(move |_| Box::new(missing_const_for_fn::MissingConstForFn::new(msrv()))); store.register_late_pass(move |_| Box::new(needless_question_mark::NeedlessQuestionMark)); - store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv))); - store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv))); + store.register_late_pass(move |_| Box::new(casts::Casts::new(msrv()))); + store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv()))); store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount)); store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod)); let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length; store.register_late_pass(move |_| { Box::new(index_refutable_slice::IndexRefutableSlice::new( max_suggested_slice_pattern_length, - msrv, + msrv(), )) }); store.register_late_pass(|_| Box::<shadow::Shadow>::default()); @@ -648,7 +610,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(borrow_deref_ref::BorrowDerefRef)); store.register_late_pass(|_| Box::new(no_effect::NoEffect)); store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment)); - store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv))); + store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv()))); let cognitive_complexity_threshold = conf.cognitive_complexity_threshold; store.register_late_pass(move |_| { Box::new(cognitive_complexity::CognitiveComplexity::new( @@ -806,7 +768,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports))); store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default()); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); - store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv))); + store.register_late_pass(move |_| Box::new(dereference::Dereferencing::new(msrv()))); store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse)); store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend)); store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex)); @@ -840,7 +802,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::<vec_init_then_push::VecInitThenPush>::default()); store.register_late_pass(|_| Box::new(redundant_slicing::RedundantSlicing)); store.register_late_pass(|_| Box::new(from_str_radix_10::FromStrRadix10)); - store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv))); + store.register_late_pass(move |_| Box::new(if_then_some_else_none::IfThenSomeElseNone::new(msrv()))); store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison)); store.register_early_pass(move || Box::new(module_style::ModStyle)); store.register_late_pass(|_| Box::new(unused_async::UnusedAsync)); @@ -865,14 +827,15 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: )) }); store.register_late_pass(move |_| Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks)); - store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv))); + let allow_mixed_uninlined = conf.allow_mixed_uninlined_format_args; + store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined))); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); 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(manual_bits::ManualBits::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv()))); store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default()); let allow_dbg_in_tests = conf.allow_dbg_in_tests; @@ -896,20 +859,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_early_pass(|| Box::<duplicate_mod::DuplicateMod>::default()); store.register_early_pass(|| Box::new(unused_rounding::UnusedRounding)); - store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv))); + store.register_early_pass(move || Box::new(almost_complete_letter_range::AlmostCompleteLetterRange::new(msrv()))); store.register_late_pass(|_| Box::new(swap_ptr_to_ref::SwapPtrToRef)); store.register_late_pass(|_| Box::new(mismatching_type_param_order::TypeParamMismatch)); store.register_late_pass(|_| Box::new(read_zero_byte_vec::ReadZeroByteVec)); store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)); - store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv))); - store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv()))); + store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv()))); let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold; store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(|_| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked)); store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default()); - store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv))); + store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv()))); store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone)); - store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(msrv()))); store.register_late_pass(|_| Box::new(manual_string_new::ManualStringNew)); store.register_late_pass(|_| Box::new(unused_peekable::UnusedPeekable)); store.register_early_pass(|| Box::new(multi_assignments::MultiAssignments)); @@ -920,7 +883,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|_| Box::new(missing_trait_methods::MissingTraitMethods)); store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); - store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv))); + store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv()))); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/clippy_lints/src/lifetimes.rs b/clippy_lints/src/lifetimes.rs index 220941dcd5d..7cf1a6b8084 100644 --- a/clippy_lints/src/lifetimes.rs +++ b/clippy_lints/src/lifetimes.rs @@ -3,7 +3,7 @@ use clippy_utils::trait_ref_of_method; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter}; use rustc_hir::intravisit::{ - walk_fn_decl, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, + walk_fn_decl, walk_generic_arg, walk_generic_param, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound, walk_poly_trait_ref, walk_trait_ref, walk_ty, Visitor, }; use rustc_hir::lang_items; @@ -481,7 +481,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { sub_visitor.visit_fn_decl(decl); self.nested_elision_site_lts.append(&mut sub_visitor.all_lts()); }, - TyKind::TraitObject(bounds, ref lt, _) => { + TyKind::TraitObject(bounds, lt, _) => { if !lt.is_elided() { self.unelided_trait_object_lifetime = true; } @@ -497,14 +497,7 @@ impl<'a, 'tcx> Visitor<'tcx> for RefVisitor<'a, 'tcx> { if let GenericArg::Lifetime(l) = generic_arg && let LifetimeName::Param(def_id) = l.res { self.lifetime_generic_arg_spans.entry(def_id).or_insert(l.ident.span); } - // Replace with `walk_generic_arg` if/when https://github.com/rust-lang/rust/pull/103692 lands. - // walk_generic_arg(self, generic_arg); - match generic_arg { - GenericArg::Lifetime(lt) => self.visit_lifetime(lt), - GenericArg::Type(ty) => self.visit_ty(ty), - GenericArg::Const(ct) => self.visit_anon_const(&ct.value), - GenericArg::Infer(inf) => self.visit_infer(inf), - } + walk_generic_arg(self, generic_arg); } } diff --git a/clippy_lints/src/manual_bits.rs b/clippy_lints/src/manual_bits.rs index 6655c92b1da..462d73cf0b9 100644 --- a/clippy_lints/src/manual_bits.rs +++ b/clippy_lints/src/manual_bits.rs @@ -1,12 +1,12 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::get_parent_expr; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{get_parent_expr, meets_msrv, msrvs}; 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}; use rustc_span::sym; @@ -34,12 +34,12 @@ declare_clippy_lint! { #[derive(Clone)] pub struct ManualBits { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl ManualBits { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -48,7 +48,7 @@ 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, msrvs::MANUAL_BITS) { + if !self.msrv.meets(msrvs::MANUAL_BITS) { return; } diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs index 02dc8755dd6..bb6d628af3b 100644 --- a/clippy_lints/src/manual_clamp.rs +++ b/clippy_lints/src/manual_clamp.rs @@ -1,28 +1,25 @@ +use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::higher::If; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::sugg::Sugg; +use clippy_utils::ty::implements_trait; +use clippy_utils::visitors::is_const_evaluatable; +use clippy_utils::MaybePath; +use clippy_utils::{ + eq_expr_value, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks, peel_blocks_with_stmt, +}; use itertools::Itertools; +use rustc_errors::Applicability; use rustc_errors::Diagnostic; use rustc_hir::{ def::Res, Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::Ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{symbol::sym, Span}; use std::ops::Deref; -use clippy_utils::{ - diagnostics::{span_lint_and_then, span_lint_hir_and_then}, - eq_expr_value, - higher::If, - is_diag_trait_item, is_trait_method, meets_msrv, msrvs, path_res, path_to_local_id, peel_blocks, - peel_blocks_with_stmt, - sugg::Sugg, - ty::implements_trait, - visitors::is_const_evaluatable, - MaybePath, -}; -use rustc_errors::Applicability; - declare_clippy_lint! { /// ### What it does /// Identifies good opportunities for a clamp function from std or core, and suggests using it. @@ -87,11 +84,11 @@ declare_clippy_lint! { impl_lint_pass!(ManualClamp => [MANUAL_CLAMP]); pub struct ManualClamp { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl ManualClamp { - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -114,7 +111,7 @@ struct InputMinMax<'tcx> { impl<'tcx> LateLintPass<'tcx> for ManualClamp { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !meets_msrv(self.msrv, msrvs::CLAMP) { + if !self.msrv.meets(msrvs::CLAMP) { return; } if !expr.span.from_expansion() { @@ -130,7 +127,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp { } fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { - if !meets_msrv(self.msrv, msrvs::CLAMP) { + if !self.msrv.meets(msrvs::CLAMP) { return; } for suggestion in is_two_if_pattern(cx, block) { diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs index bb8c142f8e4..5ab049d8d13 100644 --- a/clippy_lints/src/manual_is_ascii_check.rs +++ b/clippy_lints/src/manual_is_ascii_check.rs @@ -1,15 +1,12 @@ +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::{diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, source::snippet}; use rustc_ast::LitKind::{Byte, Char}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, PatKind, RangeEnd}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{def_id::DefId, sym}; -use clippy_utils::{ - diagnostics::span_lint_and_sugg, in_constant, macros::root_macro_call, meets_msrv, msrvs, source::snippet, -}; - declare_clippy_lint! { /// ### What it does /// Suggests to use dedicated built-in methods, @@ -45,12 +42,12 @@ declare_clippy_lint! { impl_lint_pass!(ManualIsAsciiCheck => [MANUAL_IS_ASCII_CHECK]); pub struct ManualIsAsciiCheck { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl ManualIsAsciiCheck { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -70,11 +67,11 @@ enum CharRange { impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT) { + if !self.msrv.meets(msrvs::IS_ASCII_DIGIT) { return; } - if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::IS_ASCII_DIGIT_CONST) { + if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) { return; } diff --git a/clippy_lints/src/manual_let_else.rs b/clippy_lints/src/manual_let_else.rs index 1846596fa4c..874d36ca9f4 100644 --- a/clippy_lints/src/manual_let_else.rs +++ b/clippy_lints/src/manual_let_else.rs @@ -1,16 +1,16 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::IfLetOrMatch; -use clippy_utils::source::snippet_opt; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::peel_blocks; +use clippy_utils::source::snippet_with_context; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::visitors::{for_each_expr, Descend}; -use clippy_utils::{meets_msrv, msrvs, peel_blocks}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; use rustc_span::Span; @@ -50,13 +50,13 @@ declare_clippy_lint! { } pub struct ManualLetElse { - msrv: Option<RustcVersion>, + msrv: Msrv, matches_behaviour: MatchLintBehaviour, } impl ManualLetElse { #[must_use] - pub fn new(msrv: Option<RustcVersion>, matches_behaviour: MatchLintBehaviour) -> Self { + pub fn new(msrv: Msrv, matches_behaviour: MatchLintBehaviour) -> Self { Self { msrv, matches_behaviour, @@ -69,7 +69,7 @@ impl_lint_pass!(ManualLetElse => [MANUAL_LET_ELSE]); impl<'tcx> LateLintPass<'tcx> for ManualLetElse { fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) { let if_let_or_match = if_chain! { - if meets_msrv(self.msrv, msrvs::LET_ELSE); + if self.msrv.meets(msrvs::LET_ELSE); if !in_external_macro(cx.sess(), stmt.span); if let StmtKind::Local(local) = stmt.kind; if let Some(init) = local.init; @@ -141,20 +141,18 @@ fn emit_manual_let_else(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, pat: // * unused binding collision detection with existing ones // * putting patterns with at the top level | inside () // for this to be machine applicable. - let app = Applicability::HasPlaceholders; + let mut app = Applicability::HasPlaceholders; + let (sn_pat, _) = snippet_with_context(cx, pat.span, span.ctxt(), "", &mut app); + let (sn_expr, _) = snippet_with_context(cx, expr.span, span.ctxt(), "", &mut app); + let (sn_else, _) = snippet_with_context(cx, else_body.span, span.ctxt(), "", &mut app); - if let Some(sn_pat) = snippet_opt(cx, pat.span) && - let Some(sn_expr) = snippet_opt(cx, expr.span) && - let Some(sn_else) = snippet_opt(cx, else_body.span) - { - let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) { - sn_else - } else { - format!("{{ {sn_else} }}") - }; - let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};"); - diag.span_suggestion(span, "consider writing", sugg, app); - } + let else_bl = if matches!(else_body.kind, ExprKind::Block(..)) { + sn_else.into_owned() + } else { + format!("{{ {sn_else} }}") + }; + let sugg = format!("let {sn_pat} = {sn_expr} else {else_bl};"); + diag.span_suggestion(span, "consider writing", sugg, app); }, ); } diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs index 4877cee0cc1..bca193be9e7 100644 --- a/clippy_lints/src/manual_non_exhaustive.rs +++ b/clippy_lints/src/manual_non_exhaustive.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; +use clippy_utils::is_doc_hidden; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; -use clippy_utils::{is_doc_hidden, meets_msrv, msrvs}; use rustc_ast::ast::{self, VisibilityKind}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -8,7 +9,6 @@ use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::{self as hir, Expr, ExprKind, QPath}; use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; use rustc_middle::ty::DefIdTree; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{sym, Span}; @@ -63,12 +63,12 @@ declare_clippy_lint! { #[expect(clippy::module_name_repetitions)] pub struct ManualNonExhaustiveStruct { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl ManualNonExhaustiveStruct { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -77,14 +77,14 @@ impl_lint_pass!(ManualNonExhaustiveStruct => [MANUAL_NON_EXHAUSTIVE]); #[expect(clippy::module_name_repetitions)] pub struct ManualNonExhaustiveEnum { - msrv: Option<RustcVersion>, + msrv: Msrv, constructed_enum_variants: FxHashSet<(DefId, DefId)>, potential_enums: Vec<(LocalDefId, LocalDefId, Span, Span)>, } impl ManualNonExhaustiveEnum { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv, constructed_enum_variants: FxHashSet::default(), @@ -97,7 +97,7 @@ impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]); impl EarlyLintPass for ManualNonExhaustiveStruct { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) { + if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) { return; } @@ -149,7 +149,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct { impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - if !meets_msrv(self.msrv, msrvs::NON_EXHAUSTIVE) { + if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) { return; } diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs index 6f25a2ed8e4..8d447c37150 100644 --- a/clippy_lints/src/manual_rem_euclid.rs +++ b/clippy_lints/src/manual_rem_euclid.rs @@ -1,12 +1,12 @@ use clippy_utils::consts::{constant_full_int, FullInt}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{in_constant, meets_msrv, msrvs, path_to_local}; +use clippy_utils::{in_constant, path_to_local}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -34,12 +34,12 @@ declare_clippy_lint! { } pub struct ManualRemEuclid { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl ManualRemEuclid { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -48,11 +48,11 @@ impl_lint_pass!(ManualRemEuclid => [MANUAL_REM_EUCLID]); impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::REM_EUCLID) { + if !self.msrv.meets(msrvs::REM_EUCLID) { return; } - if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::REM_EUCLID_CONST) { + if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::REM_EUCLID_CONST) { return; } diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs index d6438ca7fec..c1e6c82487d 100644 --- a/clippy_lints/src/manual_retain.rs +++ b/clippy_lints/src/manual_retain.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq}; -use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_hir::def_id::DefId; @@ -50,12 +50,12 @@ declare_clippy_lint! { } pub struct ManualRetain { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl ManualRetain { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -71,9 +71,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualRetain { && let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind && let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id) && match_def_path(cx, collect_def_id, &paths::CORE_ITER_COLLECT) { - check_into_iter(cx, parent_expr, left_expr, target_expr, self.msrv); - check_iter(cx, parent_expr, left_expr, target_expr, self.msrv); - check_to_owned(cx, parent_expr, left_expr, target_expr, self.msrv); + check_into_iter(cx, parent_expr, left_expr, target_expr, &self.msrv); + check_iter(cx, parent_expr, left_expr, target_expr, &self.msrv); + check_to_owned(cx, parent_expr, left_expr, target_expr, &self.msrv); } } @@ -85,7 +85,7 @@ fn check_into_iter( parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>, - msrv: Option<RustcVersion>, + msrv: &Msrv, ) { if let hir::ExprKind::MethodCall(_, into_iter_expr, [_], _) = &target_expr.kind && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) @@ -104,7 +104,7 @@ fn check_iter( parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>, - msrv: Option<RustcVersion>, + msrv: &Msrv, ) { if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind && let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) @@ -127,9 +127,9 @@ fn check_to_owned( parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, target_expr: &hir::Expr<'_>, - msrv: Option<RustcVersion>, + msrv: &Msrv, ) { - if meets_msrv(msrv, msrvs::STRING_RETAIN) + if msrv.meets(msrvs::STRING_RETAIN) && let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind && let Some(to_owned_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id) && match_def_path(cx, to_owned_def_id, &paths::TO_OWNED_METHOD) @@ -215,10 +215,10 @@ fn match_acceptable_def_path(cx: &LateContext<'_>, collect_def_id: DefId) -> boo .any(|&method| match_def_path(cx, collect_def_id, method)) } -fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Option<RustcVersion>) -> bool { +fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv) -> bool { let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs(); ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| { is_type_diagnostic_item(cx, expr_ty, *ty) - && acceptable_msrv.map_or(true, |acceptable_msrv| meets_msrv(msrv, acceptable_msrv)) + && acceptable_msrv.map_or(true, |acceptable_msrv| msrv.meets(acceptable_msrv)) }) } diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs index 0976940afac..de166b9765f 100644 --- a/clippy_lints/src/manual_strip.rs +++ b/clippy_lints/src/manual_strip.rs @@ -1,8 +1,9 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; use clippy_utils::usage::mutated_variables; -use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, msrvs, paths}; +use clippy_utils::{eq_expr_value, higher, match_def_path, paths}; use if_chain::if_chain; use rustc_ast::ast::LitKind; use rustc_hir::def::Res; @@ -11,7 +12,6 @@ use rustc_hir::BinOpKind; use rustc_hir::{BorrowKind, Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Spanned; use rustc_span::Span; @@ -48,12 +48,12 @@ declare_clippy_lint! { } pub struct ManualStrip { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl ManualStrip { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -68,7 +68,7 @@ enum StripKind { impl<'tcx> LateLintPass<'tcx> for ManualStrip { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { - if !meets_msrv(self.msrv, msrvs::STR_STRIP_PREFIX) { + if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) { return; } diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs index 7d8171ead89..7b15a307fec 100644 --- a/clippy_lints/src/matches/mod.rs +++ b/clippy_lints/src/matches/mod.rs @@ -23,13 +23,13 @@ mod single_match; mod try_err; mod wild_in_or_pats; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet_opt, walk_span_to_context}; -use clippy_utils::{higher, in_constant, is_span_match, meets_msrv, msrvs}; +use clippy_utils::{higher, in_constant, is_span_match}; use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat}; use rustc_lexer::{tokenize, TokenKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{Span, SpanData, SyntaxContext}; @@ -930,13 +930,13 @@ declare_clippy_lint! { #[derive(Default)] pub struct Matches { - msrv: Option<RustcVersion>, + msrv: Msrv, infallible_destructuring_match_linted: bool, } impl Matches { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv, ..Matches::default() @@ -1000,9 +1000,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) { if source == MatchSource::Normal { - if !(meets_msrv(self.msrv, msrvs::MATCHES_MACRO) - && match_like_matches::check_match(cx, expr, ex, arms)) - { + if !(self.msrv.meets(msrvs::MATCHES_MACRO) && match_like_matches::check_match(cx, expr, ex, arms)) { match_same_arms::check(cx, arms); } @@ -1034,7 +1032,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches { collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else); if !from_expansion { if let Some(else_expr) = if_let.if_else { - if meets_msrv(self.msrv, msrvs::MATCHES_MACRO) { + if self.msrv.meets(msrvs::MATCHES_MACRO) { match_like_matches::check_if_let( cx, expr, diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs index c6cba81d871..704c34c32bf 100644 --- a/clippy_lints/src/matches/try_err.rs +++ b/clippy_lints/src/matches/try_err.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{get_parent_expr, is_res_lang_ctor, match_def_path, path_res, paths}; +use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::ResultErr; @@ -107,7 +107,7 @@ fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { if_chain! { if let ty::Adt(def, subst) = ty.kind(); - if match_def_path(cx, def.did(), &paths::POLL); + if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()); let ready_ty = subst.type_at(0); if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); @@ -124,7 +124,7 @@ fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option< fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> { if_chain! { if let ty::Adt(def, subst) = ty.kind(); - if match_def_path(cx, def.did(), &paths::POLL); + if cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did()); let ready_ty = subst.type_at(0); if let ty::Adt(ready_def, ready_subst) = ready_ty.kind(); diff --git a/clippy_lints/src/mem_replace.rs b/clippy_lints/src/mem_replace.rs index 0c4d9f100f7..35024ec1224 100644 --- a/clippy_lints/src/mem_replace.rs +++ b/clippy_lints/src/mem_replace.rs @@ -1,14 +1,14 @@ use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::ty::is_non_aggregate_primitive_type; -use clippy_utils::{is_default_equivalent, is_res_lang_ctor, meets_msrv, msrvs, path_res}; +use clippy_utils::{is_default_equivalent, is_res_lang_ctor, path_res}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir::LangItem::OptionNone; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::Span; use rustc_span::symbol::sym; @@ -227,12 +227,12 @@ fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr< } pub struct MemReplace { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl MemReplace { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for MemReplace { then { check_replace_option_with_none(cx, src, dest, expr.span); check_replace_with_uninit(cx, src, dest, expr.span); - if meets_msrv(self.msrv, msrvs::MEM_TAKE) { + if self.msrv.meets(msrvs::MEM_TAKE) { check_replace_with_default(cx, src, dest, expr.span); } } diff --git a/clippy_lints/src/methods/cloned_instead_of_copied.rs b/clippy_lints/src/methods/cloned_instead_of_copied.rs index e9aeab2d5b6..4e6ec61f6a8 100644 --- a/clippy_lints/src/methods/cloned_instead_of_copied.rs +++ b/clippy_lints/src/methods/cloned_instead_of_copied.rs @@ -1,25 +1,25 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_trait_method; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::{get_iterator_item_ty, is_copy}; -use clippy_utils::{is_trait_method, meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_span::{sym, Span}; use super::CLONED_INSTEAD_OF_COPIED; -pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: Option<RustcVersion>) { +pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, msrv: &Msrv) { let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); let inner_ty = match recv_ty.kind() { // `Option<T>` -> `T` ty::Adt(adt, subst) - if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && meets_msrv(msrv, msrvs::OPTION_COPIED) => + if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) && msrv.meets(msrvs::OPTION_COPIED) => { subst.type_at(0) }, - _ if is_trait_method(cx, expr, sym::Iterator) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) => { + _ if is_trait_method(cx, expr, sym::Iterator) && msrv.meets(msrvs::ITERATOR_COPIED) => { match get_iterator_item_ty(cx, recv_ty) { // <T as Iterator>::Item Some(ty) => ty, diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs index 720d9a68c85..ae03da0d3f9 100644 --- a/clippy_lints/src/methods/err_expect.rs +++ b/clippy_lints/src/methods/err_expect.rs @@ -1,27 +1,27 @@ use super::ERR_EXPECT; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::has_debug_impl; -use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item}; +use clippy_utils::ty::is_type_diagnostic_item; use rustc_errors::Applicability; use rustc_lint::LateContext; use rustc_middle::ty; use rustc_middle::ty::Ty; -use rustc_semver::RustcVersion; use rustc_span::{sym, Span}; pub(super) fn check( cx: &LateContext<'_>, _expr: &rustc_hir::Expr<'_>, recv: &rustc_hir::Expr<'_>, - msrv: Option<RustcVersion>, expect_span: Span, err_span: Span, + msrv: &Msrv, ) { if_chain! { if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); // Test the version to make sure the lint can be showed (expect_err has been // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982) - if meets_msrv(msrv, msrvs::EXPECT_ERR); + if msrv.meets(msrvs::EXPECT_ERR); // Grabs the `Result<T, E>` type let result_type = cx.typeck_results().expr_ty(recv); diff --git a/clippy_lints/src/methods/filter_map_next.rs b/clippy_lints/src/methods/filter_map_next.rs index ddf8a1f09b8..175e04f8ac0 100644 --- a/clippy_lints/src/methods/filter_map_next.rs +++ b/clippy_lints/src/methods/filter_map_next.rs @@ -1,10 +1,10 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::is_trait_method; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; -use clippy_utils::{is_trait_method, meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_semver::RustcVersion; use rustc_span::sym; use super::FILTER_MAP_NEXT; @@ -14,10 +14,10 @@ pub(super) fn check<'tcx>( expr: &'tcx hir::Expr<'_>, recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, - msrv: Option<RustcVersion>, + msrv: &Msrv, ) { if is_trait_method(cx, expr, sym::Iterator) { - if !meets_msrv(msrv, msrvs::ITERATOR_FIND_MAP) { + if !msrv.meets(msrvs::ITERATOR_FIND_MAP) { return; } diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs index 304024e8066..301aff5ae6a 100644 --- a/clippy_lints/src/methods/is_digit_ascii_radix.rs +++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs @@ -1,23 +1,22 @@ //! Lint for `c.is_digit(10)` use super::IS_DIGIT_ASCII_RADIX; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::{ - consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, meets_msrv, msrvs, - source::snippet_with_applicability, + consts::constant_full_int, consts::FullInt, diagnostics::span_lint_and_sugg, source::snippet_with_applicability, }; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_semver::RustcVersion; pub(super) fn check<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, self_arg: &'tcx Expr<'_>, radix: &'tcx Expr<'_>, - msrv: Option<RustcVersion>, + msrv: &Msrv, ) { - if !meets_msrv(msrv, msrvs::IS_ASCII_DIGIT) { + if !msrv.meets(msrvs::IS_ASCII_DIGIT) { return; } diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs index 6bc783c6d50..52cc1e0464b 100644 --- a/clippy_lints/src/methods/map_clone.rs +++ b/clippy_lints/src/methods/map_clone.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::{is_copy, is_type_diagnostic_item}; -use clippy_utils::{is_diag_trait_item, meets_msrv, msrvs, peel_blocks}; +use clippy_utils::{is_diag_trait_item, peel_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; @@ -9,19 +10,12 @@ use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty; use rustc_middle::ty::adjustment::Adjust; -use rustc_semver::RustcVersion; use rustc_span::symbol::Ident; use rustc_span::{sym, Span}; use super::MAP_CLONE; -pub(super) fn check( - cx: &LateContext<'_>, - e: &hir::Expr<'_>, - recv: &hir::Expr<'_>, - arg: &hir::Expr<'_>, - msrv: Option<RustcVersion>, -) { +pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_>, arg: &hir::Expr<'_>, msrv: &Msrv) { if_chain! { if let Some(method_id) = cx.typeck_results().type_dependent_def_id(e.hir_id); if cx.tcx.impl_of_method(method_id) @@ -97,10 +91,10 @@ fn lint_needless_cloning(cx: &LateContext<'_>, root: Span, receiver: Span) { ); } -fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: Option<RustcVersion>) { +fn lint_explicit_closure(cx: &LateContext<'_>, replace: Span, root: Span, is_copy: bool, msrv: &Msrv) { let mut applicability = Applicability::MachineApplicable; - let (message, sugg_method) = if is_copy && meets_msrv(msrv, msrvs::ITERATOR_COPIED) { + let (message, sugg_method) = if is_copy && msrv.meets(msrvs::ITERATOR_COPIED) { ("you are using an explicit closure for copying elements", "copied") } else { ("you are using an explicit closure for cloning elements", "cloned") diff --git a/clippy_lints/src/methods/map_unwrap_or.rs b/clippy_lints/src/methods/map_unwrap_or.rs index 74fdead216b..3122f72ee91 100644 --- a/clippy_lints/src/methods/map_unwrap_or.rs +++ b/clippy_lints/src/methods/map_unwrap_or.rs @@ -1,12 +1,11 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::usage::mutated_variables; -use clippy_utils::{meets_msrv, msrvs}; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; -use rustc_semver::RustcVersion; use rustc_span::symbol::sym; use super::MAP_UNWRAP_OR; @@ -19,13 +18,13 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, map_arg: &'tcx hir::Expr<'_>, unwrap_arg: &'tcx hir::Expr<'_>, - msrv: Option<RustcVersion>, + msrv: &Msrv, ) -> bool { // lint if the caller of `map()` is an `Option` let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option); let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); - if is_result && !meets_msrv(msrv, msrvs::RESULT_MAP_OR_ELSE) { + if is_result && !msrv.meets(msrvs::RESULT_MAP_OR_ELSE) { return false; } diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs index 38165ab4fb2..d2913680cbb 100644 --- a/clippy_lints/src/methods/mod.rs +++ b/clippy_lints/src/methods/mod.rs @@ -104,8 +104,9 @@ mod zst_offset; use bind_instead_of_map::BindInsteadOfMap; use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item}; -use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, meets_msrv, msrvs, return_ty}; +use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, return_ty}; use if_chain::if_chain; use rustc_hir as hir; use rustc_hir::{Expr, ExprKind, TraitItem, TraitItemKind}; @@ -113,7 +114,6 @@ use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::lint::in_external_macro; use rustc_middle::ty::{self, TraitRef, Ty}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::{sym, Span}; @@ -3163,7 +3163,7 @@ declare_clippy_lint! { pub struct Methods { avoid_breaking_exported_api: bool, - msrv: Option<RustcVersion>, + msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, } @@ -3172,7 +3172,7 @@ impl Methods { #[must_use] pub fn new( avoid_breaking_exported_api: bool, - msrv: Option<RustcVersion>, + msrv: Msrv, allow_expect_in_tests: bool, allow_unwrap_in_tests: bool, ) -> Self { @@ -3325,7 +3325,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods { single_char_add_str::check(cx, expr, receiver, args); into_iter_on_ref::check(cx, expr, method_span, method_call.ident.name, receiver); single_char_pattern::check(cx, expr, method_call.ident.name, receiver, args); - unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, self.msrv); + unnecessary_to_owned::check(cx, expr, method_call.ident.name, receiver, args, &self.msrv); }, hir::ExprKind::Binary(op, lhs, rhs) if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne => { let mut info = BinaryExprInfo { @@ -3501,7 +3501,7 @@ impl Methods { ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv), ("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, self.msrv), + ("cloned", []) => cloned_instead_of_copied::check(cx, expr, recv, span, &self.msrv), ("collect", []) if is_trait_method(cx, expr, sym::Iterator) => { needless_collect::check(cx, span, expr, recv, call_span); match method_call(recv) { @@ -3512,7 +3512,7 @@ impl Methods { map_collect_result_unit::check(cx, expr, m_recv, m_arg); }, Some(("take", take_self_arg, [take_arg], _, _)) => { - if meets_msrv(self.msrv, msrvs::STR_REPEAT) { + if self.msrv.meets(msrvs::STR_REPEAT) { manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); } }, @@ -3539,7 +3539,7 @@ impl Methods { }, ("expect", [_]) => match method_call(recv) { Some(("ok", recv, [], _, _)) => ok_expect::check(cx, expr, recv), - Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, self.msrv, span, err_span), + Some(("err", recv, [], err_span, _)) => err_expect::check(cx, expr, recv, span, err_span, &self.msrv), _ => expect_used::check(cx, expr, recv, false, self.allow_expect_in_tests), }, ("expect_err", [_]) => expect_used::check(cx, expr, recv, true, self.allow_expect_in_tests), @@ -3578,7 +3578,7 @@ impl Methods { unit_hash::check(cx, expr, recv, arg); }, ("is_file", []) => filetype_is_file::check(cx, expr, recv), - ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, self.msrv), + ("is_digit", [radix]) => is_digit_ascii_radix::check(cx, expr, recv, radix, &self.msrv), ("is_none", []) => check_is_some_is_none(cx, expr, recv, false), ("is_some", []) => check_is_some_is_none(cx, expr, recv, true), ("iter" | "iter_mut" | "into_iter", []) => { @@ -3601,7 +3601,7 @@ impl Methods { }, (name @ ("map" | "map_err"), [m_arg]) => { if name == "map" { - map_clone::check(cx, expr, recv, m_arg, self.msrv); + map_clone::check(cx, expr, recv, m_arg, &self.msrv); if let Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) = method_call(recv) { iter_kv_map::check(cx, map_name, expr, recv2, m_arg); } @@ -3610,8 +3610,8 @@ impl Methods { } 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, self.msrv), - ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, self.msrv), + ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, &self.msrv), + ("as_ref", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, false, &self.msrv), ("filter", [f_arg]) => { filter_map::check(cx, expr, recv2, f_arg, span2, recv, m_arg, span, false); }, @@ -3632,7 +3632,7 @@ impl Methods { match (name2, args2) { ("cloned", []) => iter_overeager_cloned::check(cx, expr, recv, recv2, false, false), ("filter", [arg]) => filter_next::check(cx, expr, recv2, arg), - ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, self.msrv), + ("filter_map", [arg]) => filter_map_next::check(cx, expr, recv2, arg, &self.msrv), ("iter", []) => iter_next_slice::check(cx, expr, recv2), ("skip", [arg]) => iter_skip_next::check(cx, expr, recv2, arg), ("skip_while", [_]) => skip_while_next::check(cx, expr), @@ -3680,10 +3680,10 @@ impl Methods { vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span); }, ("seek", [arg]) => { - if meets_msrv(self.msrv, msrvs::SEEK_FROM_CURRENT) { + if self.msrv.meets(msrvs::SEEK_FROM_CURRENT) { seek_from_current::check(cx, expr, recv, arg); } - if meets_msrv(self.msrv, msrvs::SEEK_REWIND) { + if self.msrv.meets(msrvs::SEEK_REWIND) { seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span); } }, @@ -3699,7 +3699,7 @@ impl Methods { ("splitn" | "rsplitn", [count_arg, pat_arg]) => { if let Some((Constant::Int(count), _)) = constant(cx, cx.typeck_results(), count_arg) { suspicious_splitn::check(cx, name, expr, recv, count); - str_splitn::check(cx, name, expr, recv, pat_arg, count, self.msrv); + str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv); } }, ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => { @@ -3717,7 +3717,7 @@ impl Methods { }, ("take", []) => needless_option_take::check(cx, expr, recv), ("then", [arg]) => { - if !meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) { + if !self.msrv.meets(msrvs::BOOL_THEN_SOME) { return; } unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some"); @@ -3760,7 +3760,7 @@ impl Methods { }, ("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, self.msrv) => {}, + if map_unwrap_or::check(cx, expr, recv, map_arg, u_arg, &self.msrv) => {}, _ => { unwrap_or_else_default::check(cx, expr, recv, u_arg); unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); diff --git a/clippy_lints/src/methods/option_as_ref_deref.rs b/clippy_lints/src/methods/option_as_ref_deref.rs index e6eb64bcbde..3e33f919337 100644 --- a/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/clippy_lints/src/methods/option_as_ref_deref.rs @@ -1,13 +1,13 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; use clippy_utils::ty::is_type_diagnostic_item; -use clippy_utils::{match_def_path, meets_msrv, msrvs, path_to_local_id, paths, peel_blocks}; +use clippy_utils::{match_def_path, path_to_local_id, paths, peel_blocks}; use if_chain::if_chain; use rustc_errors::Applicability; use rustc_hir as hir; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_span::sym; use super::OPTION_AS_REF_DEREF; @@ -19,9 +19,9 @@ pub(super) fn check( as_ref_recv: &hir::Expr<'_>, map_arg: &hir::Expr<'_>, is_mut: bool, - msrv: Option<RustcVersion>, + msrv: &Msrv, ) { - if !meets_msrv(msrv, msrvs::OPTION_AS_DEREF) { + if !msrv.meets(msrvs::OPTION_AS_DEREF) { return; } diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index 1acac59144c..3c01ce1fecd 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -1,9 +1,10 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_context; use clippy_utils::usage::local_used_after_expr; use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; -use clippy_utils::{is_diag_item_method, match_def_path, meets_msrv, msrvs, path_to_local_id, paths}; +use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths}; use core::ops::ControlFlow; use if_chain::if_chain; use rustc_errors::Applicability; @@ -12,7 +13,6 @@ use rustc_hir::{ }; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_span::{sym, Span, Symbol, SyntaxContext}; use super::{MANUAL_SPLIT_ONCE, NEEDLESS_SPLITN}; @@ -24,7 +24,7 @@ pub(super) fn check( self_arg: &Expr<'_>, pat_arg: &Expr<'_>, count: u128, - msrv: Option<RustcVersion>, + msrv: &Msrv, ) { if count < 2 || !cx.typeck_results().expr_ty_adjusted(self_arg).peel_refs().is_str() { return; @@ -34,7 +34,7 @@ pub(super) fn check( IterUsageKind::Nth(n) => count > n + 1, IterUsageKind::NextTuple => count > 2, }; - let manual = count == 2 && meets_msrv(msrv, msrvs::STR_SPLIT_ONCE); + let manual = count == 2 && msrv.meets(msrvs::STR_SPLIT_ONCE); match parse_iter_usage(cx, expr.span.ctxt(), cx.tcx.hir().parent_iter(expr.hir_id)) { Some(usage) if needless(usage.kind) => lint_needless(cx, method_name, expr, self_arg, pat_arg), diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs index 7ff13b95626..17b0507682a 100644 --- a/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -1,13 +1,11 @@ use super::implicit_clone::is_clone_like; use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_opt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_mid_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; -use clippy_utils::{ - fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty, -}; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty}; use rustc_errors::Applicability; use rustc_hir::{def_id::DefId, BorrowKind, Expr, ExprKind, ItemKind, Node}; use rustc_hir_typeck::{FnCtxt, Inherited}; @@ -16,14 +14,9 @@ use rustc_lint::LateContext; use rustc_middle::mir::Mutability; use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; use rustc_middle::ty::subst::{GenericArg, GenericArgKind, SubstsRef}; -use rustc_middle::ty::EarlyBinder; -use rustc_middle::ty::{self, Clause, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty}; -use rustc_semver::RustcVersion; +use rustc_middle::ty::{self, Clause, EarlyBinder, ParamTy, PredicateKind, ProjectionPredicate, TraitPredicate, Ty}; use rustc_span::{sym, Symbol}; -use rustc_trait_selection::traits::{ - query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause, -}; -use std::cmp::max; +use rustc_trait_selection::traits::{query::evaluate_obligation::InferCtxtExt as _, Obligation, ObligationCause}; use super::UNNECESSARY_TO_OWNED; @@ -33,7 +26,7 @@ pub fn check<'tcx>( method_name: Symbol, receiver: &'tcx Expr<'_>, args: &'tcx [Expr<'_>], - msrv: Option<RustcVersion>, + msrv: &Msrv, ) { if_chain! { if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); @@ -204,7 +197,7 @@ fn check_into_iter_call_arg( expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>, - msrv: Option<RustcVersion>, + msrv: &Msrv, ) -> bool { if_chain! { if let Some(parent) = get_parent_expr(cx, expr); @@ -219,7 +212,7 @@ fn check_into_iter_call_arg( 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) && meets_msrv(msrv, msrvs::ITERATOR_COPIED) { + let cloned_or_copied = if is_copy(cx, item_ty) && msrv.meets(msrvs::ITERATOR_COPIED) { "copied" } else { "cloned" @@ -267,11 +260,22 @@ fn check_other_call_arg<'tcx>( if let Some(as_ref_trait_id) = cx.tcx.get_diagnostic_item(sym::AsRef); if trait_predicate.def_id() == deref_trait_id || trait_predicate.def_id() == as_ref_trait_id; let receiver_ty = cx.typeck_results().expr_ty(receiver); - if can_change_type(cx, maybe_arg, receiver_ty); // We can't add an `&` when the trait is `Deref` because `Target = &T` won't match // `Target = T`. - if n_refs > 0 || is_copy(cx, receiver_ty) || trait_predicate.def_id() != deref_trait_id; - let n_refs = max(n_refs, usize::from(!is_copy(cx, receiver_ty))); + if let Some((n_refs, receiver_ty)) = if n_refs > 0 || is_copy(cx, receiver_ty) { + Some((n_refs, receiver_ty)) + } else if trait_predicate.def_id() != deref_trait_id { + Some((1, cx.tcx.mk_ref( + cx.tcx.lifetimes.re_erased, + ty::TypeAndMut { + ty: receiver_ty, + mutbl: Mutability::Not, + }, + ))) + } else { + None + }; + if can_change_type(cx, maybe_arg, receiver_ty); if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); then { span_lint_and_sugg( @@ -345,13 +349,13 @@ fn get_input_traits_and_projections<'tcx>( if trait_predicate.trait_ref.self_ty() == input { trait_predicates.push(trait_predicate); } - } + }, PredicateKind::Clause(Clause::Projection(projection_predicate)) => { if projection_predicate.projection_ty.self_ty() == input { projection_predicates.push(projection_predicate); } - } - _ => {} + }, + _ => {}, } } (trait_predicates, projection_predicates) @@ -403,10 +407,12 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty< let mut trait_predicates = cx.tcx.param_env(callee_def_id) .caller_bounds().iter().filter(|predicate| { - if let PredicateKind::Clause(Clause::Trait(trait_predicate)) = predicate.kind().skip_binder() - && trait_predicate.trait_ref.self_ty() == *param_ty { - true - } else { + if let PredicateKind::Clause(Clause::Trait(trait_predicate)) + = predicate.kind().skip_binder() + && trait_predicate.trait_ref.self_ty() == *param_ty + { + true + } else { false } }); @@ -466,12 +472,7 @@ fn is_cloned_or_copied(cx: &LateContext<'_>, method_name: Symbol, method_def_id: /// Returns true if the named method can be used to convert the receiver to its "owned" /// representation. -fn is_to_owned_like<'a>( - cx: &LateContext<'a>, - call_expr: &Expr<'a>, - method_name: Symbol, - method_def_id: DefId, -) -> bool { +fn is_to_owned_like<'a>(cx: &LateContext<'a>, call_expr: &Expr<'a>, method_name: Symbol, method_def_id: DefId) -> bool { is_clone_like(cx, method_name.as_str(), method_def_id) || is_cow_into_owned(cx, method_name, method_def_id) || is_to_string_on_string_like(cx, call_expr, method_name, method_def_id) diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs index 71cc0d0a81c..5bc04bc17fb 100644 --- a/clippy_lints/src/missing_const_for_fn.rs +++ b/clippy_lints/src/missing_const_for_fn.rs @@ -1,9 +1,8 @@ use clippy_utils::diagnostics::span_lint; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::qualify_min_const_fn::is_min_const_fn; use clippy_utils::ty::has_drop; -use clippy_utils::{ - fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, meets_msrv, msrvs, trait_ref_of_method, -}; +use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method}; use rustc_hir as hir; use rustc_hir::def_id::CRATE_DEF_ID; use rustc_hir::intravisit::FnKind; @@ -11,7 +10,6 @@ use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId}; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -75,12 +73,12 @@ declare_clippy_lint! { impl_lint_pass!(MissingConstForFn => [MISSING_CONST_FOR_FN]); pub struct MissingConstForFn { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl MissingConstForFn { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -95,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { span: Span, hir_id: HirId, ) { - if !meets_msrv(self.msrv, msrvs::CONST_IF_MATCH) { + if !self.msrv.meets(msrvs::CONST_IF_MATCH) { return; } @@ -152,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn { let mir = cx.tcx.optimized_mir(def_id); - if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, self.msrv) { + if let Err((span, err)) = is_min_const_fn(cx.tcx, mir, &self.msrv) { if cx.tcx.is_const_fn_raw(def_id.to_def_id()) { cx.tcx.sess.span_err(span, err.as_ref()); } diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs index 75e12715458..2f0b7ce16e5 100644 --- a/clippy_lints/src/needless_pass_by_value.rs +++ b/clippy_lints/src/needless_pass_by_value.rs @@ -1,7 +1,9 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::ptr::get_spans; use clippy_utils::source::{snippet, snippet_opt}; -use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item}; +use clippy_utils::ty::{ + implements_trait, implements_trait_with_env, is_copy, is_type_diagnostic_item, is_type_lang_item, +}; use clippy_utils::{get_trait_def_id, is_self, paths}; use if_chain::if_chain; use rustc_ast::ast::Attribute; @@ -124,7 +126,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { .filter_map(|obligation| { // Note that we do not want to deal with qualified predicates here. match obligation.predicate.kind().no_bound_vars() { - Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => Some(pred), + Some(ty::PredicateKind::Clause(ty::Clause::Trait(pred))) if pred.def_id() != sized_trait => { + Some(pred) + }, _ => None, } }) diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs index 819646bb678..79c1ae4861e 100644 --- a/clippy_lints/src/no_effect.rs +++ b/clippy_lints/src/no_effect.rs @@ -6,7 +6,8 @@ use clippy_utils::ty::has_drop; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{is_range_literal, BinOpKind, BlockCheckMode, Expr, ExprKind, PatKind, Stmt, StmtKind, UnsafeSource}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_middle::lint::in_external_macro; use rustc_session::{declare_lint_pass, declare_tool_lint}; use std::ops::Deref; @@ -159,8 +160,11 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if_chain! { if let StmtKind::Semi(expr) = stmt.kind; + let ctxt = stmt.span.ctxt(); + if expr.span.ctxt() == ctxt; if let Some(reduced) = reduce_expression(cx, expr); - if !&reduced.iter().any(|e| e.span.from_expansion()); + if !in_external_macro(cx.sess(), stmt.span); + if reduced.iter().all(|e| e.span.ctxt() == ctxt); then { if let ExprKind::Index(..) = &expr.kind { let snippet = if let (Some(arr), Some(func)) = diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs index 92920bbad6e..e395ff54cb1 100644 --- a/clippy_lints/src/ptr.rs +++ b/clippy_lints/src/ptr.rs @@ -490,7 +490,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( ty_name: name.ident.name, method_renames, ref_prefix: RefPrefix { - lt: lt.clone(), + lt: *lt, mutability, }, deref_ty, @@ -693,9 +693,10 @@ fn matches_preds<'tcx>( cx.tcx, ObligationCause::dummy(), cx.param_env, - cx.tcx.mk_predicate(Binder::dummy( - PredicateKind::Clause(Clause::Projection(p.with_self_ty(cx.tcx, ty))), - )), + cx.tcx + .mk_predicate(Binder::dummy(PredicateKind::Clause(Clause::Projection( + p.with_self_ty(cx.tcx, ty), + )))), )), ExistentialPredicate::AutoTrait(p) => infcx .type_implements_trait(p, [ty], cx.param_env) diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs index c6fbb5e805a..0a1b9d173cf 100644 --- a/clippy_lints/src/ranges.rs +++ b/clippy_lints/src/ranges.rs @@ -1,16 +1,16 @@ use clippy_utils::consts::{constant, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::higher; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability}; use clippy_utils::sugg::Sugg; -use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local}; +use clippy_utils::{get_parent_expr, in_constant, is_integer_const, path_to_local}; use if_chain::if_chain; use rustc_ast::ast::RangeLimits; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, HirId}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::source_map::{Span, Spanned}; use std::cmp::Ordering; @@ -161,12 +161,12 @@ declare_clippy_lint! { } pub struct Ranges { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl Ranges { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -181,7 +181,7 @@ impl_lint_pass!(Ranges => [ impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, l, r) = expr.kind { - if meets_msrv(self.msrv, msrvs::RANGE_CONTAINS) { + if self.msrv.meets(msrvs::RANGE_CONTAINS) { check_possible_range_contains(cx, op.node, l, r, expr, expr.span); } } diff --git a/clippy_lints/src/redundant_closure_call.rs b/clippy_lints/src/redundant_closure_call.rs index 8e675d34a18..2a42e73488f 100644 --- a/clippy_lints/src/redundant_closure_call.rs +++ b/clippy_lints/src/redundant_closure_call.rs @@ -81,8 +81,8 @@ impl EarlyLintPass for RedundantClosureCall { "try not to call a closure in the expression where it is declared", |diag| { if fn_decl.inputs.is_empty() { - let app = Applicability::MachineApplicable; - let mut hint = Sugg::ast(cx, body, ".."); + let mut app = Applicability::MachineApplicable; + let mut hint = Sugg::ast(cx, body, "..", closure.span.ctxt(), &mut app); if asyncness.is_async() { // `async x` is a syntax error, so it becomes `async { x }` diff --git a/clippy_lints/src/redundant_field_names.rs b/clippy_lints/src/redundant_field_names.rs index 40b03068f6c..61bff4a0e38 100644 --- a/clippy_lints/src/redundant_field_names.rs +++ b/clippy_lints/src/redundant_field_names.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{meets_msrv, msrvs}; +use clippy_utils::msrvs::{self, Msrv}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_middle::lint::in_external_macro; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -37,12 +36,12 @@ declare_clippy_lint! { } pub struct RedundantFieldNames { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl RedundantFieldNames { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -51,7 +50,7 @@ impl_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]); impl EarlyLintPass for RedundantFieldNames { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if !meets_msrv(self.msrv, msrvs::FIELD_INIT_SHORTHAND) { + if !self.msrv.meets(msrvs::FIELD_INIT_SHORTHAND) { return; } diff --git a/clippy_lints/src/redundant_static_lifetimes.rs b/clippy_lints/src/redundant_static_lifetimes.rs index 60ba62c4a43..3aa2490bc44 100644 --- a/clippy_lints/src/redundant_static_lifetimes.rs +++ b/clippy_lints/src/redundant_static_lifetimes.rs @@ -1,10 +1,9 @@ use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet; -use clippy_utils::{meets_msrv, msrvs}; use rustc_ast::ast::{Item, ItemKind, Ty, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; declare_clippy_lint! { @@ -34,12 +33,12 @@ declare_clippy_lint! { } pub struct RedundantStaticLifetimes { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl RedundantStaticLifetimes { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -96,7 +95,7 @@ impl RedundantStaticLifetimes { impl EarlyLintPass for RedundantStaticLifetimes { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { - if !meets_msrv(self.msrv, msrvs::STATIC_IN_CONST) { + if !self.msrv.meets(msrvs::STATIC_IN_CONST) { return; } diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs index 2b2a41d1601..81143d7799e 100644 --- a/clippy_lints/src/returns.rs +++ b/clippy_lints/src/returns.rs @@ -12,6 +12,7 @@ use rustc_middle::lint::in_external_macro; use rustc_middle::ty::subst::GenericArgKind; use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_span::source_map::Span; +use rustc_span::{BytePos, Pos}; declare_clippy_lint! { /// ### What it does @@ -209,13 +210,14 @@ fn check_final_expr<'tcx>( if cx.tcx.hir().attrs(expr.hir_id).is_empty() { let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner)); if !borrows { - emit_return_lint( - cx, - peeled_drop_expr.span, - semi_spans, - inner.as_ref().map(|i| i.span), - replacement, - ); + // check if expr return nothing + let ret_span = if inner.is_none() && replacement == RetReplacement::Empty { + extend_span_to_previous_non_ws(cx, peeled_drop_expr.span) + } else { + peeled_drop_expr.span + }; + + emit_return_lint(cx, ret_span, semi_spans, inner.as_ref().map(|i| i.span), replacement); } } }, @@ -289,3 +291,16 @@ fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) }) .is_some() } + +// Go backwards while encountering whitespace and extend the given Span to that point. +fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span { + if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) { + let ws = [' ', '\t', '\n']; + if let Some(non_ws_pos) = prev_source.rfind(|c| !ws.contains(&c)) { + let len = prev_source.len() - non_ws_pos - 1; + return sp.with_lo(sp.lo() - BytePos::from_usize(len)); + } + } + + sp +} diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs index 424a6e9264e..83e651aba8e 100644 --- a/clippy_lints/src/transmute/mod.rs +++ b/clippy_lints/src/transmute/mod.rs @@ -16,10 +16,10 @@ mod utils; mod wrong_transmute; use clippy_utils::in_constant; +use clippy_utils::msrvs::Msrv; use if_chain::if_chain; use rustc_hir::{Expr, ExprKind, QPath}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::symbol::sym; @@ -410,7 +410,7 @@ declare_clippy_lint! { } pub struct Transmute { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl_lint_pass!(Transmute => [ CROSSPOINTER_TRANSMUTE, @@ -431,7 +431,7 @@ impl_lint_pass!(Transmute => [ ]); impl Transmute { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -461,7 +461,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute { let linted = wrong_transmute::check(cx, e, from_ty, to_ty) | crosspointer_transmute::check(cx, e, from_ty, to_ty) | transmuting_null::check(cx, e, arg, to_ty) - | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv) + | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, &self.msrv) | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context) | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg) diff --git a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs index 12d0b866e1c..3dde4eee671 100644 --- a/clippy_lints/src/transmute/transmute_ptr_to_ref.rs +++ b/clippy_lints/src/transmute/transmute_ptr_to_ref.rs @@ -1,12 +1,12 @@ use super::TRANSMUTE_PTR_TO_REF; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::source::snippet_with_applicability; -use clippy_utils::{meets_msrv, msrvs, sugg}; +use clippy_utils::sugg; use rustc_errors::Applicability; use rustc_hir::{self as hir, Expr, GenericArg, Mutability, Path, TyKind}; use rustc_lint::LateContext; use rustc_middle::ty::{self, Ty, TypeVisitable}; -use rustc_semver::RustcVersion; /// Checks for `transmute_ptr_to_ref` lint. /// Returns `true` if it's triggered, otherwise returns `false`. @@ -17,7 +17,7 @@ pub(super) fn check<'tcx>( to_ty: Ty<'tcx>, arg: &'tcx Expr<'_>, path: &'tcx Path<'_>, - msrv: Option<RustcVersion>, + msrv: &Msrv, ) -> bool { match (&from_ty.kind(), &to_ty.kind()) { (ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => { @@ -37,7 +37,7 @@ pub(super) fn check<'tcx>( let sugg = if let Some(ty) = get_explicit_type(path) { let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app); - if meets_msrv(msrv, msrvs::POINTER_CAST) { + if msrv.meets(msrvs::POINTER_CAST) { format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_par()) } else if from_ptr_ty.has_erased_regions() { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string() @@ -46,7 +46,7 @@ pub(super) fn check<'tcx>( } } else if from_ptr_ty.ty == *to_ref_ty { if from_ptr_ty.has_erased_regions() { - if meets_msrv(msrv, msrvs::POINTER_CAST) { + if msrv.meets(msrvs::POINTER_CAST) { format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_par()) } else { sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))) diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs index e8f15a44473..2e1b6d8d4ea 100644 --- a/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -1,6 +1,10 @@ +use std::ops::ControlFlow; + use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::source::walk_span_to_context; +use clippy_utils::visitors::{for_each_expr_with_closures, Descend}; use clippy_utils::{get_parent_node, is_lint_allowed}; +use hir::HirId; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource}; @@ -59,11 +63,39 @@ declare_clippy_lint! { restriction, "creating an unsafe block without explaining why it is safe" } +declare_clippy_lint! { + /// ### What it does + /// Checks for `// SAFETY: ` comments on safe code. + /// + /// ### Why is this bad? + /// Safe code has no safety requirements, so there is no need to + /// describe safety invariants. + /// + /// ### Example + /// ```rust + /// use std::ptr::NonNull; + /// let a = &mut 42; + /// + /// // SAFETY: references are guaranteed to be non-null. + /// let ptr = NonNull::new(a).unwrap(); + /// ``` + /// Use instead: + /// ```rust + /// use std::ptr::NonNull; + /// let a = &mut 42; + /// + /// let ptr = NonNull::new(a).unwrap(); + /// ``` + #[clippy::version = "1.67.0"] + pub UNNECESSARY_SAFETY_COMMENT, + restriction, + "annotating safe code with a safety comment" +} -declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]); +declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS, UNNECESSARY_SAFETY_COMMENT]); -impl LateLintPass<'_> for UndocumentedUnsafeBlocks { - fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) { +impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks { + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) && !in_external_macro(cx.tcx.sess, block.span) && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id) @@ -87,35 +119,175 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks { "consider adding a safety comment on the preceding line", ); } + + if let Some(tail) = block.expr + && !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, tail.hir_id) + && !in_external_macro(cx.tcx.sess, tail.span) + && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, tail.span, tail.hir_id) + && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos) + { + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + tail.span, + "expression has unnecessary safety comment", + Some(help_span), + "consider removing the safety comment", + ); + } } - fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if let hir::ItemKind::Impl(imple) = item.kind - && imple.unsafety == hir::Unsafety::Unsafe - && !in_external_macro(cx.tcx.sess, item.span) - && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id()) - && !is_unsafe_from_proc_macro(cx, item.span) - && !item_has_safety_comment(cx, item) + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &hir::Stmt<'tcx>) { + let ( + hir::StmtKind::Local(&hir::Local { init: Some(expr), .. }) + | hir::StmtKind::Expr(expr) + | hir::StmtKind::Semi(expr) + ) = stmt.kind else { return }; + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id) + && !in_external_macro(cx.tcx.sess, stmt.span) + && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id) + && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos) { + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + stmt.span, + "statement has unnecessary safety comment", + Some(help_span), + "consider removing the safety comment", + ); + } + } + + fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { + if in_external_macro(cx.tcx.sess, item.span) { + return; + } + + let mk_spans = |pos: BytePos| { let source_map = cx.tcx.sess.source_map(); + let span = Span::new(pos, pos, SyntaxContext::root(), None); + let help_span = source_map.span_extend_to_next_char(span, '\n', true); let span = if source_map.is_multiline(item.span) { source_map.span_until_char(item.span, '\n') } else { item.span }; + (span, help_span) + }; - span_lint_and_help( - cx, - UNDOCUMENTED_UNSAFE_BLOCKS, - span, - "unsafe impl missing a safety comment", - None, - "consider adding a safety comment on the preceding line", - ); + let item_has_safety_comment = item_has_safety_comment(cx, item); + match (&item.kind, item_has_safety_comment) { + // lint unsafe impl without safety comment + (hir::ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.unsafety == hir::Unsafety::Unsafe => { + if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id()) + && !is_unsafe_from_proc_macro(cx, item.span) + { + let source_map = cx.tcx.sess.source_map(); + let span = if source_map.is_multiline(item.span) { + source_map.span_until_char(item.span, '\n') + } else { + item.span + }; + + span_lint_and_help( + cx, + UNDOCUMENTED_UNSAFE_BLOCKS, + span, + "unsafe impl missing a safety comment", + None, + "consider adding a safety comment on the preceding line", + ); + } + }, + // lint safe impl with unnecessary safety comment + (hir::ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.unsafety == hir::Unsafety::Normal => { + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { + let (span, help_span) = mk_spans(pos); + + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + "impl has unnecessary safety comment", + Some(help_span), + "consider removing the safety comment", + ); + } + }, + (hir::ItemKind::Impl(_), _) => {}, + // const and static items only need a safety comment if their body is an unsafe block, lint otherwise + (&hir::ItemKind::Const(.., body) | &hir::ItemKind::Static(.., body), HasSafetyComment::Yes(pos)) => { + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) { + let body = cx.tcx.hir().body(body); + if !matches!( + body.value.kind, hir::ExprKind::Block(block, _) + if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) + ) { + let (span, help_span) = mk_spans(pos); + + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + &format!("{} has unnecessary safety comment", item.kind.descr()), + Some(help_span), + "consider removing the safety comment", + ); + } + } + }, + // Aside from unsafe impls and consts/statics with an unsafe block, items in general + // do not have safety invariants that need to be documented, so lint those. + (_, HasSafetyComment::Yes(pos)) => { + if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) { + let (span, help_span) = mk_spans(pos); + + span_lint_and_help( + cx, + UNNECESSARY_SAFETY_COMMENT, + span, + &format!("{} has unnecessary safety comment", item.kind.descr()), + Some(help_span), + "consider removing the safety comment", + ); + } + }, + _ => (), } } } +fn expr_has_unnecessary_safety_comment<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + comment_pos: BytePos, +) -> Option<Span> { + // this should roughly be the reverse of `block_parents_have_safety_comment` + if for_each_expr_with_closures(cx, expr, |expr| match expr.kind { + hir::ExprKind::Block( + Block { + rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), + .. + }, + _, + ) => ControlFlow::Break(()), + // statements will be handled by check_stmt itself again + hir::ExprKind::Block(..) => ControlFlow::Continue(Descend::No), + _ => ControlFlow::Continue(Descend::Yes), + }) + .is_some() + { + return None; + } + + let source_map = cx.tcx.sess.source_map(); + let span = Span::new(comment_pos, comment_pos, SyntaxContext::root(), None); + let help_span = source_map.span_extend_to_next_char(span, '\n', true); + + Some(help_span) +} + fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool { let source_map = cx.sess().source_map(); let file_pos = source_map.lookup_byte_offset(span.lo()); @@ -170,85 +342,134 @@ fn block_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { // won't work. This is to avoid dealing with where such a comment should be place relative to // attributes and doc comments. - span_from_macro_expansion_has_safety_comment(cx, span) || span_in_body_has_safety_comment(cx, span) + matches!( + span_from_macro_expansion_has_safety_comment(cx, span), + HasSafetyComment::Yes(_) + ) || span_in_body_has_safety_comment(cx, span) +} + +enum HasSafetyComment { + Yes(BytePos), + No, + Maybe, } /// Checks if the lines immediately preceding the item contain a safety comment. #[allow(clippy::collapsible_match)] -fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool { - if span_from_macro_expansion_has_safety_comment(cx, item.span) { - return true; +fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSafetyComment { + match span_from_macro_expansion_has_safety_comment(cx, item.span) { + HasSafetyComment::Maybe => (), + has_safety_comment => return has_safety_comment, } - if item.span.ctxt() == SyntaxContext::root() { - if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) { - let comment_start = match parent_node { - Node::Crate(parent_mod) => { - comment_start_before_impl_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item) - }, - Node::Item(parent_item) => { - if let ItemKind::Mod(parent_mod) = &parent_item.kind { - comment_start_before_impl_in_mod(cx, parent_mod, parent_item.span, item) - } else { - // Doesn't support impls in this position. Pretend a comment was found. - return true; - } - }, - Node::Stmt(stmt) => { - if let Some(stmt_parent) = get_parent_node(cx.tcx, stmt.hir_id) { - match stmt_parent { - Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo), - _ => { - // Doesn't support impls in this position. Pretend a comment was found. - return true; - }, - } - } else { - // Problem getting the parent node. Pretend a comment was found. - return true; - } - }, - _ => { + if item.span.ctxt() != SyntaxContext::root() { + return HasSafetyComment::No; + } + if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) { + let comment_start = match parent_node { + Node::Crate(parent_mod) => { + comment_start_before_item_in_mod(cx, parent_mod, parent_mod.spans.inner_span, item) + }, + Node::Item(parent_item) => { + if let ItemKind::Mod(parent_mod) = &parent_item.kind { + comment_start_before_item_in_mod(cx, parent_mod, parent_item.span, item) + } else { // Doesn't support impls in this position. Pretend a comment was found. - return true; - }, - }; + return HasSafetyComment::Maybe; + } + }, + Node::Stmt(stmt) => { + if let Some(Node::Block(block)) = get_parent_node(cx.tcx, stmt.hir_id) { + walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo) + } else { + // Problem getting the parent node. Pretend a comment was found. + return HasSafetyComment::Maybe; + } + }, + _ => { + // Doesn't support impls in this position. Pretend a comment was found. + return HasSafetyComment::Maybe; + }, + }; - let source_map = cx.sess().source_map(); - if let Some(comment_start) = comment_start - && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo()) - && let Ok(comment_start_line) = source_map.lookup_line(comment_start) - && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) - && let Some(src) = unsafe_line.sf.src.as_deref() - { - unsafe_line.sf.lines(|lines| { - comment_start_line.line < unsafe_line.line && text_has_safety_comment( + let source_map = cx.sess().source_map(); + if let Some(comment_start) = comment_start + && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo()) + && let Ok(comment_start_line) = source_map.lookup_line(comment_start) + && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + return unsafe_line.sf.lines(|lines| { + if comment_start_line.line >= unsafe_line.line { + HasSafetyComment::No + } else { + match text_has_safety_comment( src, &lines[comment_start_line.line + 1..=unsafe_line.line], unsafe_line.sf.start_pos.to_usize(), - ) - }) - } else { - // Problem getting source text. Pretend a comment was found. - true - } - } else { - // No parent node. Pretend a comment was found. - true + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, + } + } + }); + } + } + HasSafetyComment::Maybe +} + +/// Checks if the lines immediately preceding the item contain a safety comment. +#[allow(clippy::collapsible_match)] +fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> HasSafetyComment { + match span_from_macro_expansion_has_safety_comment(cx, span) { + HasSafetyComment::Maybe => (), + has_safety_comment => return has_safety_comment, + } + + if span.ctxt() != SyntaxContext::root() { + return HasSafetyComment::No; + } + + if let Some(parent_node) = get_parent_node(cx.tcx, hir_id) { + let comment_start = match parent_node { + Node::Block(block) => walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo), + _ => return HasSafetyComment::Maybe, + }; + + let source_map = cx.sess().source_map(); + if let Some(comment_start) = comment_start + && let Ok(unsafe_line) = source_map.lookup_line(span.lo()) + && let Ok(comment_start_line) = source_map.lookup_line(comment_start) + && Lrc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf) + && let Some(src) = unsafe_line.sf.src.as_deref() + { + return unsafe_line.sf.lines(|lines| { + if comment_start_line.line >= unsafe_line.line { + HasSafetyComment::No + } else { + match text_has_safety_comment( + src, + &lines[comment_start_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, + } + } + }); } - } else { - false } + HasSafetyComment::Maybe } -fn comment_start_before_impl_in_mod( +fn comment_start_before_item_in_mod( cx: &LateContext<'_>, parent_mod: &hir::Mod<'_>, parent_mod_span: Span, - imple: &hir::Item<'_>, + item: &hir::Item<'_>, ) -> Option<BytePos> { parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| { - if *item_id == imple.item_id() { + if *item_id == item.item_id() { if idx == 0 { // mod A { /* comment */ unsafe impl T {} ... } // ^------------------------------------------^ returns the start of this span @@ -270,11 +491,11 @@ fn comment_start_before_impl_in_mod( }) } -fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { +fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span) -> HasSafetyComment { let source_map = cx.sess().source_map(); let ctxt = span.ctxt(); if ctxt == SyntaxContext::root() { - false + HasSafetyComment::Maybe } else { // From a macro expansion. Get the text from the start of the macro declaration to start of the // unsafe block. @@ -286,15 +507,22 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span && let Some(src) = unsafe_line.sf.src.as_deref() { unsafe_line.sf.lines(|lines| { - macro_line.line < unsafe_line.line && text_has_safety_comment( - src, - &lines[macro_line.line + 1..=unsafe_line.line], - unsafe_line.sf.start_pos.to_usize(), - ) + if macro_line.line < unsafe_line.line { + match text_has_safety_comment( + src, + &lines[macro_line.line + 1..=unsafe_line.line], + unsafe_line.sf.start_pos.to_usize(), + ) { + Some(b) => HasSafetyComment::Yes(b), + None => HasSafetyComment::No, + } + } else { + HasSafetyComment::No + } }) } else { // Problem getting source text. Pretend a comment was found. - true + HasSafetyComment::Maybe } } } @@ -333,7 +561,7 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { src, &lines[body_line.line + 1..=unsafe_line.line], unsafe_line.sf.start_pos.to_usize(), - ) + ).is_some() }) } else { // Problem getting source text. Pretend a comment was found. @@ -345,30 +573,34 @@ fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool { } /// Checks if the given text has a safety comment for the immediately proceeding line. -fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool { +fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> Option<BytePos> { let mut lines = line_starts .array_windows::<2>() .rev() .map_while(|[start, end]| { let start = start.to_usize() - offset; let end = end.to_usize() - offset; - src.get(start..end).map(|text| (start, text.trim_start())) + let text = src.get(start..end)?; + let trimmed = text.trim_start(); + Some((start + (text.len() - trimmed.len()), trimmed)) }) .filter(|(_, text)| !text.is_empty()); let Some((line_start, line)) = lines.next() else { - return false; + return None; }; // Check for a sequence of line comments. if line.starts_with("//") { - let mut line = line; + let (mut line, mut line_start) = (line, line_start); loop { if line.to_ascii_uppercase().contains("SAFETY:") { - return true; + return Some(BytePos( + u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(), + )); } match lines.next() { - Some((_, x)) if x.starts_with("//") => line = x, - _ => return false, + Some((s, x)) if x.starts_with("//") => (line, line_start) = (x, s), + _ => return None, } } } @@ -377,16 +609,19 @@ fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> let (mut line_start, mut line) = (line_start, line); loop { if line.starts_with("/*") { - let src = src[line_start..line_starts.last().unwrap().to_usize() - offset].trim_start(); + let src = &src[line_start..line_starts.last().unwrap().to_usize() - offset]; let mut tokens = tokenize(src); - return src[..tokens.next().unwrap().len as usize] + return (src[..tokens.next().unwrap().len as usize] .to_ascii_uppercase() .contains("SAFETY:") - && tokens.all(|t| t.kind == TokenKind::Whitespace); + && tokens.all(|t| t.kind == TokenKind::Whitespace)) + .then_some(BytePos( + u32::try_from(line_start).unwrap() + u32::try_from(offset).unwrap(), + )); } match lines.next() { Some(x) => (line_start, line) = x, - None => return false, + None => return None, } } } diff --git a/clippy_lints/src/unnested_or_patterns.rs b/clippy_lints/src/unnested_or_patterns.rs index bb6fb38e969..7355260ae4a 100644 --- a/clippy_lints/src/unnested_or_patterns.rs +++ b/clippy_lints/src/unnested_or_patterns.rs @@ -2,14 +2,14 @@ use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{meets_msrv, msrvs, over}; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::over; use rustc_ast::mut_visit::*; use rustc_ast::ptr::P; use rustc_ast::{self as ast, Mutability, Pat, PatKind, PatKind::*, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::DUMMY_SP; @@ -45,14 +45,13 @@ declare_clippy_lint! { "unnested or-patterns, e.g., `Foo(Bar) | Foo(Baz) instead of `Foo(Bar | Baz)`" } -#[derive(Clone, Copy)] pub struct UnnestedOrPatterns { - msrv: Option<RustcVersion>, + msrv: Msrv, } impl UnnestedOrPatterns { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv } } } @@ -61,13 +60,13 @@ impl_lint_pass!(UnnestedOrPatterns => [UNNESTED_OR_PATTERNS]); impl EarlyLintPass for UnnestedOrPatterns { fn check_arm(&mut self, cx: &EarlyContext<'_>, a: &ast::Arm) { - if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { + if self.msrv.meets(msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &a.pat); } } fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { - if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { + if self.msrv.meets(msrvs::OR_PATTERNS) { if let ast::ExprKind::Let(pat, _, _) = &e.kind { lint_unnested_or_patterns(cx, pat); } @@ -75,13 +74,13 @@ impl EarlyLintPass for UnnestedOrPatterns { } fn check_param(&mut self, cx: &EarlyContext<'_>, p: &ast::Param) { - if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { + if self.msrv.meets(msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &p.pat); } } fn check_local(&mut self, cx: &EarlyContext<'_>, l: &ast::Local) { - if meets_msrv(self.msrv, msrvs::OR_PATTERNS) { + if self.msrv.meets(msrvs::OR_PATTERNS) { lint_unnested_or_patterns(cx, &l.pat); } } diff --git a/clippy_lints/src/unused_rounding.rs b/clippy_lints/src/unused_rounding.rs index aac6719a8dc..097568cd1f7 100644 --- a/clippy_lints/src/unused_rounding.rs +++ b/clippy_lints/src/unused_rounding.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::source::snippet; use rustc_ast::ast::{Expr, ExprKind, MethodCall}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -29,22 +30,16 @@ declare_clippy_lint! { } declare_lint_pass!(UnusedRounding => [UNUSED_ROUNDING]); -fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { +fn is_useless_rounding<'a>(cx: &EarlyContext<'_>, expr: &'a Expr) -> Option<(&'a str, String)> { if let ExprKind::MethodCall(box MethodCall { seg:name_ident, receiver, .. }) = &expr.kind && let method_name = name_ident.ident.name.as_str() && (method_name == "ceil" || method_name == "round" || method_name == "floor") && let ExprKind::Lit(token_lit) = &receiver.kind - && token_lit.is_semantic_float() { - let mut f_str = token_lit.symbol.to_string(); - let f = f_str.trim_end_matches('_').parse::<f64>().unwrap(); - if let Some(suffix) = token_lit.suffix { - f_str.push_str(suffix.as_str()); - } - if f.fract() == 0.0 { - Some((method_name, f_str)) - } else { - None - } + && token_lit.is_semantic_float() + && let Ok(f) = token_lit.symbol.as_str().replace('_', "").parse::<f64>() { + (f.fract() == 0.0).then(|| + (method_name, snippet(cx, receiver.span, "..").to_string()) + ) } else { None } @@ -52,7 +47,7 @@ fn is_useless_rounding(expr: &Expr) -> Option<(&str, String)> { impl EarlyLintPass for UnusedRounding { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if let Some((method_name, float)) = is_useless_rounding(expr) { + if let Some((method_name, float)) = is_useless_rounding(cx, expr) { span_lint_and_sugg( cx, UNUSED_ROUNDING, diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs index e2860db71a5..4c755d812a0 100644 --- a/clippy_lints/src/use_self.rs +++ b/clippy_lints/src/use_self.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::is_from_proc_macro; +use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::ty::same_type_and_consts; -use clippy_utils::{is_from_proc_macro, meets_msrv, msrvs}; use if_chain::if_chain; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -14,7 +15,6 @@ use rustc_hir::{ }; use rustc_hir_analysis::hir_ty_to_ty; use rustc_lint::{LateContext, LateLintPass}; -use rustc_semver::RustcVersion; use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_span::Span; @@ -57,13 +57,13 @@ declare_clippy_lint! { #[derive(Default)] pub struct UseSelf { - msrv: Option<RustcVersion>, + msrv: Msrv, stack: Vec<StackItem>, } impl UseSelf { #[must_use] - pub fn new(msrv: Option<RustcVersion>) -> Self { + pub fn new(msrv: Msrv) -> Self { Self { msrv, ..Self::default() @@ -199,7 +199,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>) { if_chain! { if !hir_ty.span.from_expansion(); - if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, in_body, @@ -228,7 +228,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { if_chain! { if !expr.span.from_expansion(); - if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); if cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id); then {} else { return; } @@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf { fn check_pat(&mut self, cx: &LateContext<'_>, pat: &Pat<'_>) { if_chain! { if !pat.span.from_expansion(); - if meets_msrv(self.msrv, msrvs::TYPE_ALIAS_ENUM_VARIANTS); + if self.msrv.meets(msrvs::TYPE_ALIAS_ENUM_VARIANTS); if let Some(&StackItem::Check { impl_id, .. }) = self.stack.last(); // get the path from the pattern if let PatKind::Path(QPath::Resolved(_, path)) diff --git a/clippy_lints/src/utils/conf.rs b/clippy_lints/src/utils/conf.rs index b37d4239477..b6dc8cd7ab1 100644 --- a/clippy_lints/src/utils/conf.rs +++ b/clippy_lints/src/utils/conf.rs @@ -402,6 +402,10 @@ define_Conf! { /// A list of paths to types that should be treated like `Arc`, i.e. ignored but /// for the generic parameters for determining interior mutability (ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()])), + /// Lint: UNINLINED_FORMAT_ARGS. + /// + /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)` + (allow_mixed_uninlined_format_args: bool = true), } /// Search for the configuration file. diff --git a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs index 1e994e3f2b1..9876a8a765c 100644 --- a/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs +++ b/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs @@ -41,7 +41,7 @@ impl LateLintPass<'_> for MsrvAttrImpl { .type_of(f.did) .walk() .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_))) - .any(|t| match_type(cx, t.expect_ty(), &paths::RUSTC_VERSION)) + .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV)) }); if !items.iter().any(|item| item.ident.name == sym!(enter_lint_attrs)); then { |
