From d6d29df2702eb1a2084764c5103b6a06b3949d55 Mon Sep 17 00:00:00 2001 From: Ype Kingma Date: Fri, 17 May 2024 13:26:01 +0200 Subject: [PATCH 01/13] Updated to master, resolved a minor conflict, fixed a typo --- CHANGELOG.md | 2 + clippy_lints/src/declared_lints.rs | 2 + clippy_lints/src/lib.rs | 2 + .../src/lifetimes_bound_nested_ref.rs | 540 ++++++++++++++++++ .../ui/lifetimes_bound_nested_ref_expl.fixed | 69 +++ tests/ui/lifetimes_bound_nested_ref_expl.rs | 69 +++ .../ui/lifetimes_bound_nested_ref_expl.stderr | 64 +++ tests/ui/lifetimes_bound_nested_ref_impl.rs | 71 +++ .../ui/lifetimes_bound_nested_ref_impl.stderr | 64 +++ 9 files changed, 883 insertions(+) create mode 100644 clippy_lints/src/lifetimes_bound_nested_ref.rs create mode 100644 tests/ui/lifetimes_bound_nested_ref_expl.fixed create mode 100644 tests/ui/lifetimes_bound_nested_ref_expl.rs create mode 100644 tests/ui/lifetimes_bound_nested_ref_expl.stderr create mode 100644 tests/ui/lifetimes_bound_nested_ref_impl.rs create mode 100644 tests/ui/lifetimes_bound_nested_ref_impl.stderr diff --git a/CHANGELOG.md b/CHANGELOG.md index d1d70f4ddfb8..1b41b99bb7cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5453,6 +5453,7 @@ Released 2018-09-13 [`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods [`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop [`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop +[`explicit_lifetimes_bound_nested_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_lifetimes_bound_nested_ref [`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write [`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice [`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain @@ -5514,6 +5515,7 @@ Released 2018-09-13 [`impl_trait_in_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_trait_in_params [`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone [`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher +[`implicit_lifetimes_bound_nested_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_lifetimes_bound_nested_ref [`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return [`implicit_saturating_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_add [`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 3c4e75df8abe..38ca64ae505d 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -266,6 +266,8 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::let_with_type_underscore::LET_WITH_TYPE_UNDERSCORE_INFO, crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO, crate::lifetimes::NEEDLESS_LIFETIMES_INFO, + crate::lifetimes_bound_nested_ref::IMPLICIT_LIFETIMES_BOUND_INFO, + crate::lifetimes_bound_nested_ref::EXPLICIT_LIFETIMES_BOUND_INFO, crate::lines_filter_map_ok::LINES_FILTER_MAP_OK_INFO, crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO, crate::literal_representation::INCONSISTENT_DIGIT_GROUPING_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 153820140129..78fe2fd82b5d 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -195,6 +195,7 @@ mod let_if_seq; mod let_underscore; mod let_with_type_underscore; mod lifetimes; +mod lifetimes_bound_nested_ref; mod lines_filter_map_ok; mod literal_representation; mod loops; @@ -936,6 +937,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(move |_| Box::new(assigning_clones::AssigningClones::new(conf))); store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects)); store.register_late_pass(|_| Box::new(manual_unwrap_or_default::ManualUnwrapOrDefault)); + store.register_early_pass(|| Box::new(lifetimes_bound_nested_ref::LifetimesBoundNestedRef)); store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed)); store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf))); store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf))); diff --git a/clippy_lints/src/lifetimes_bound_nested_ref.rs b/clippy_lints/src/lifetimes_bound_nested_ref.rs new file mode 100644 index 000000000000..fd91ed745f3e --- /dev/null +++ b/clippy_lints/src/lifetimes_bound_nested_ref.rs @@ -0,0 +1,540 @@ +/// Lints to help dealing with unsoundness due to a compiler bug described here: +/// . +/// +/// For the following three cases the current compiler (1.76.0) gives a later error message when +/// declaring a generic lifetime bound that is implied by a nested reference: +/// +/// [Issue 25860](https://github.com/rust-lang/rust/issues/25860): +/// Implied bounds on nested references + variance = soundness hole +/// +/// [Issue 84591](https://github.com/rust-lang/rust/issues/84591): +/// HRTB on subtrait unsoundly provides HTRB on supertrait with weaker implied bounds +/// +/// [Issue 100051](https://github.com/rust-lang/rust/issues/100051): +/// Implied bounds from projections in impl header can be unsound +/// +/// The lint here suggests to declare such lifetime bounds in the hope that +/// the unsoundness is avoided. +/// +/// There is also a reverse lint that suggest to remove lifetime bounds +/// that are implied by nested references. This reverse lint is intended to be used only +/// when the compiler has been fixed to handle these lifetime bounds correctly. +use std::cmp::Ordering; +use std::collections::btree_map::Entry; +use std::collections::BTreeMap; + +use clippy_utils::diagnostics::{span_lint, span_lint_and_note, span_lint_and_then}; +use rustc_ast::visit::FnKind; +use rustc_ast::{ + AngleBracketedArg, FnRetTy, GenericArg, GenericArgs, GenericBound, GenericParamKind, Generics, Item, ItemKind, + NodeId, Path, Ty, TyKind, WherePredicate, +}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_session::impl_lint_pass; +use rustc_span::symbol::Ident; +use rustc_span::{Span, Symbol}; + +extern crate rustc_hash; +use rustc_hash::FxHashMap; + +declare_clippy_lint! { + /// ### What it does + /// Checks for nested references with declared generic lifetimes + /// in function arguments and return values and in implementation blocks. + /// Such a nested reference implies a lifetimes bound because the inner reference must + /// outlive the outer reference. + /// + /// This lint suggests to declare such implicit lifetime bounds in case they are not declared. + /// Adding such a lifetimes bound helps to avoid unsound code because this addition + /// can lead to a compiler error in related source code, as observed in rustc 1.76.0. + /// + /// The unusual way to use this lint is: + /// 1) Set the lint to warn by this clippy command line argument: + /// ```--warn clippy::explicit-lifetimes-bound``` + /// Without clippy errors, stop here. + /// 2) Add the implied lifetime bound manually, or do this automatically with these command line arguments: + /// ```--fix --warn clippy::explicit-lifetimes-bound``` + /// The code now has a declared explicit lifetimes bound that corresponds to the implied bound. + /// 3) Run the compiler on the code with this declared lifetimes bound. + /// In case the compiler now produces a compiler error on related code, + /// the compiler should already have produced this error before declaring the implied bound. + /// Leave the added lifetimes bound in the code and fix the code producing the compiler error. + /// + /// See also the reverse lint clippy::implicit-lifetimes-bound. + /// + /// ### Why is this bad? + /// The unsoundness is described + /// [here](https://github.com/rust-lang/rustc-dev-guide/blob/478a77a902f64e5128e7164e4e8a3980cfe4b133/src/traits/implied-bounds.md). + /// + /// ### Known problems + /// This lint tries to detect implied lifetime bounds for + /// [issue 25860](https://github.com/rust-lang/rust/issues/25860), + /// [issue 84591](https://github.com/rust-lang/rust/issues/84591), and + /// [issue 100051](https://github.com/rust-lang/rust/issues/100051). + /// It is not known whether this covers all cases that lead to unsoundness for implied lifetime bounds. + /// + /// The automatic fix is not extensively tested, so manually adding the implied lifetimes bound may be necessary. + /// + /// ### Example + /// Here the type of the ```val_a``` argument contains ```&'a &'b``` which implies the lifetimes bound ```'b: 'a```: + /// ```no_run + /// pub const fn lifetime_translator<'a, 'b, T>(val_a: &'a &'b (), val_b: &'b T) -> &'a T { + /// val_b + /// } + /// ``` + /// Use instead: + /// ```no_run + /// pub const fn lifetime_translator<'a, 'b: 'a, T>(val_a: &'a &'b (), val_b: &'b T) -> &'a T { + /// val_b + /// } + /// ``` + #[clippy::version = "1.81.0"] + pub EXPLICIT_LIFETIMES_BOUND, + nursery, + "declare lifetime bounds implied by nested references" +} + +declare_clippy_lint! { + /// ### What it does + /// Checks for nested references with declared generic lifetimes + /// in function arguments and return values and in implementation blocks. + /// Such a nested reference implies a lifetimes bound because the inner reference must + /// outlive the outer reference. + /// + /// This lint shows such implicit lifetime bounds in case they are declared. + /// **WARNING:** Do not remove these lifetime bounds declararations, see "Known problems" below. + /// + /// See also the reverse lint clippy::explicit-lifetimes-bound. + /// + /// ### Why is this bad? + /// The declared lifetime bounds are superfluous. + /// + /// ### Known problems + /// This lint tries to detect implied lifetime bounds for + /// [issue 25860](https://github.com/rust-lang/rust/issues/25860), + /// [issue 84591](https://github.com/rust-lang/rust/issues/84591), and + /// [issue 100051](https://github.com/rust-lang/rust/issues/100051). + /// Removing the corresponding explicitly declared lifetime bounds may lead to the unsoundness described + /// [here](https://github.com/rust-lang/rustc-dev-guide/blob/478a77a902f64e5128e7164e4e8a3980cfe4b133/src/traits/implied-bounds.md). + /// + /// Removing these redundant lifetime bounds should only be done after the compiler + /// has been fixed to deal correctly with implied lifetime bounds. + /// + /// ### Example + /// Here the type of the ```val_a``` argument contains ```&'a &'b``` which implies the lifetimes bound ```'b: 'a```: + /// ```no_run + /// pub const fn lifetime_translator<'a, 'b: 'a, T>(val_a: &'a &'b (), val_b: &'b T) -> &'a T { + /// val_b + /// } + /// ``` + /// Only after the compiler is fixed, use instead: + /// ```no_run + /// pub const fn lifetime_translator<'a, 'b, T>(val_a: &'a &'b (), val_b: &'b T) -> &'a T { + /// val_b + /// } + /// ``` + #[clippy::version = "1.79.0"] + pub IMPLICIT_LIFETIMES_BOUND, + nursery, + "detect declared lifetime bounds implied by nested references" +} + +pub struct LifetimesBoundNestedRef; + +impl_lint_pass!(LifetimesBoundNestedRef => [ + EXPLICIT_LIFETIMES_BOUND, + IMPLICIT_LIFETIMES_BOUND, +]); + +impl EarlyLintPass for LifetimesBoundNestedRef { + /// For issue 25860 + fn check_fn(&mut self, early_context: &EarlyContext<'_>, fn_kind: FnKind<'_>, _fn_span: Span, _node_id: NodeId) { + let FnKind::Fn(_fn_ctxt, _ident, fn_sig, _visibility, generics, _opt_block) = fn_kind else { + return; + }; + let declared_lifetimes_spans = get_declared_lifetimes_spans(generics); + if declared_lifetimes_spans.len() <= 1 { + return; + } + let mut linter = ImpliedBoundsLinter::new(declared_lifetimes_spans, generics); + for param in &fn_sig.decl.inputs { + linter.collect_implied_lifetime_bounds(¶m.ty); + } + if let FnRetTy::Ty(ret_ty) = &fn_sig.decl.output { + linter.collect_implied_lifetime_bounds(ret_ty); + } + linter.report_lints(early_context); + } + + /// For issues 84591 and 100051 + fn check_item_post(&mut self, early_context: &EarlyContext<'_>, item: &Item) { + let ItemKind::Impl(box_impl) = &item.kind else { + return; + }; + let Some(of_trait_ref) = &box_impl.of_trait else { + return; + }; + let declared_lifetimes = get_declared_lifetimes_spans(&box_impl.generics); + if declared_lifetimes.len() <= 1 { + return; + } + let mut linter = ImpliedBoundsLinter::new(declared_lifetimes, &box_impl.generics); + linter.collect_implied_lifetime_bounds_path(&of_trait_ref.path); + // issue 10051 for clause: impl ... for for_clause_ty + let for_clause_ty = &box_impl.self_ty; + linter.collect_implied_lifetime_bounds(for_clause_ty); + linter.report_lints(early_context); + } +} + +#[derive(Debug)] +struct BoundLftPair { + long_lft_sym: Symbol, + outlived_lft_sym: Symbol, +} + +impl BoundLftPair { + fn new(long_lft_sym: Symbol, outlived_lft_sym: Symbol) -> Self { + BoundLftPair { + long_lft_sym, + outlived_lft_sym, + } + } + + fn as_bound_declaration(&self) -> String { + format!("{}: {}", self.long_lft_sym, self.outlived_lft_sym) + } +} + +impl PartialEq for BoundLftPair { + fn eq(&self, other: &Self) -> bool { + self.long_lft_sym.eq(&other.long_lft_sym) && self.outlived_lft_sym.eq(&other.outlived_lft_sym) + } +} + +impl Eq for BoundLftPair {} + +impl PartialOrd for BoundLftPair { + fn partial_cmp(&self, other: &BoundLftPair) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for BoundLftPair { + fn cmp(&self, other: &Self) -> Ordering { + match self.long_lft_sym.cmp(&other.long_lft_sym) { + Ordering::Less => Ordering::Less, + Ordering::Greater => Ordering::Greater, + Ordering::Equal => self.outlived_lft_sym.cmp(&other.outlived_lft_sym), + } + } +} + +fn get_declared_lifetimes_spans(generics: &Generics) -> FxHashMap { + generics + .params + .iter() + .filter_map(|gp| { + if let GenericParamKind::Lifetime = gp.kind { + Some((gp.ident.name, gp.ident.span)) + } else { + None + } + }) + .collect() +} + +fn get_declared_bounds_spans(generics: &Generics) -> BTreeMap { + let mut declared_bounds = BTreeMap::new(); + generics.params.iter().for_each(|gp| { + let long_lft_sym = gp.ident.name; + gp.bounds.iter().for_each(|bound| { + if let GenericBound::Outlives(outlived_lft) = bound { + let decl_span = if let Some(colon_span) = gp.colon_span { + spans_merge(colon_span, outlived_lft.ident.span) + } else { + outlived_lft.ident.span + }; + declared_bounds.insert(BoundLftPair::new(long_lft_sym, outlived_lft.ident.name), decl_span); + } + }); + }); + generics.where_clause.predicates.iter().for_each(|wp| { + if let WherePredicate::RegionPredicate(wrp) = wp { + let long_lft_sym = wrp.lifetime.ident.name; + wrp.bounds.iter().for_each(|bound| { + if let GenericBound::Outlives(outlived_lft) = bound { + // CHECKME: how to make a good span for the lifetimes bound declaration here? + declared_bounds.insert(BoundLftPair::new(long_lft_sym, outlived_lft.ident.name), wrp.span); + } + }); + } + }); + declared_bounds +} + +#[derive(Debug)] +struct ImpliedBoundsLinter { + declared_lifetimes_spans: FxHashMap, + generics_span: Span, + declared_bounds_spans: BTreeMap, + implied_bounds_spans: BTreeMap, +} + +impl ImpliedBoundsLinter { + fn new(declared_lifetimes_spans: FxHashMap, generics: &Generics) -> Self { + ImpliedBoundsLinter { + declared_lifetimes_spans, + declared_bounds_spans: get_declared_bounds_spans(generics), + generics_span: generics.span, + implied_bounds_spans: BTreeMap::new(), + } + } + + fn collect_implied_lifetime_bounds_path(&mut self, path: &Path) { + self.collect_nested_ref_bounds_path(path, None); + } + + fn collect_nested_ref_bounds_path(&mut self, path: &Path, opt_outlived_lft_ident: Option<&Ident>) { + for path_segment in &path.segments { + if let Some(generic_args) = &path_segment.args { + if let GenericArgs::AngleBracketed(ab_args) = &**generic_args { + for ab_arg in &ab_args.args { + if let AngleBracketedArg::Arg(generic_arg) = ab_arg { + use GenericArg as GA; + match generic_arg { + GA::Lifetime(long_lft) => { + if let Some(outlived_lft_ident) = opt_outlived_lft_ident + && self.is_declared_lifetime_sym(long_lft.ident.name) + { + self.add_implied_bound_spans(&long_lft.ident, outlived_lft_ident); + } + }, + GA::Type(p_ty) => { + self.collect_nested_ref_bounds(p_ty, opt_outlived_lft_ident); + }, + GA::Const(_anon_const) => {}, + } + } + } + } + } + } + } + + fn collect_implied_lifetime_bounds(&mut self, ty: &Ty) { + self.collect_nested_ref_bounds(ty, None); + } + + fn is_declared_lifetime_sym(&self, lft_sym: Symbol) -> bool { + self.declared_lifetimes_spans.contains_key(&lft_sym) + } + + fn collect_nested_ref_bounds(&mut self, outliving_ty: &Ty, opt_outlived_lft_ident: Option<&Ident>) { + let mut outliving_tys = vec![outliving_ty]; // stack to avoid recursion + while let Some(ty) = outliving_tys.pop() { + use TyKind as TK; + match &ty.kind { + TK::Ref(opt_lifetime, referred_to_mut_ty) => { + // common to issues 25860, 84591 and 100051 + let referred_to_ty = &referred_to_mut_ty.ty; + if let Some(lifetime) = opt_lifetime + && self.is_declared_lifetime_sym(lifetime.ident.name) + { + if let Some(outlived_lft_ident) = opt_outlived_lft_ident { + self.add_implied_bound_spans(&lifetime.ident, outlived_lft_ident); + } + // recursion for nested references outliving this lifetime + self.collect_nested_ref_bounds(referred_to_ty, Some(&lifetime.ident)); + } else { + outliving_tys.push(referred_to_ty); + } + }, + TK::Slice(element_ty) => { + // not needed to detect reported issues + outliving_tys.push(element_ty); + }, + TK::Array(element_ty, _anon_const) => { + // not needed to detect reported issues + outliving_tys.push(element_ty); + }, + TK::Tup(tuple_tys) => { + // not needed to detect reported issues + for tuple_ty in tuple_tys { + outliving_tys.push(tuple_ty); + } + }, + TK::Path(opt_q_self, path) => { + if let Some(q_self) = opt_q_self { + // issue 100051 + outliving_tys.push(&q_self.ty); + } + self.collect_nested_ref_bounds_path(path, opt_outlived_lft_ident); + }, + TK::TraitObject(generic_bounds, _trait_object_syntax) => { + // dyn, not needed to detect reported issues + self.collect_nested_ref_bounds_gbs(generic_bounds, opt_outlived_lft_ident); + }, + TK::ImplTrait(_node_id, generic_bounds, _opt_capturing_args_and_span) => { + // impl, not needed to detect reported issues + self.collect_nested_ref_bounds_gbs(generic_bounds, opt_outlived_lft_ident); + }, + TK::AnonStruct(_node_id, _field_defs) | TK::AnonUnion(_node_id, _field_defs) => { + // CHECKME: can the field definition types of an anonymous struct/union have + // generic lifetimes? + }, + TK::BareFn(_bare_fn_ty) => { + // CHECKME: can bare functions have generic lifetimes? + }, + TK::CVarArgs + | TK::Dummy + | TK::Err(..) + | TK::ImplicitSelf + | TK::Infer + | TK::MacCall(..) + | TK::Never + | TK::Pat(..) + | TK::Paren(..) + | TK::Ptr(..) + | TK::Typeof(..) => {}, + } + } + } + + fn collect_nested_ref_bounds_gbs( + &mut self, + generic_bounds: &Vec, + opt_outlived_lft_ident: Option<&Ident>, + ) { + for gb in generic_bounds { + use GenericBound as GB; + match gb { + GB::Trait(poly_trait_ref, _trait_bound_modifiers) => { + for bgp in &poly_trait_ref.bound_generic_params { + use GenericParamKind as GPK; + match &bgp.kind { + GPK::Lifetime => { + if let Some(outlived_lft_ident) = opt_outlived_lft_ident + && self.is_declared_lifetime_sym(bgp.ident.name) + { + self.add_implied_bound_spans(&bgp.ident, outlived_lft_ident); + } + }, + GPK::Type { default: opt_p_ty } => { + if let Some(ty) = opt_p_ty { + self.collect_nested_ref_bounds(ty, opt_outlived_lft_ident); + } + }, + GPK::Const { ty, .. } => { + self.collect_nested_ref_bounds(ty, opt_outlived_lft_ident); + }, + } + } + }, + GB::Outlives(_lifetime) => { + // CHECKME: what is the meaning of GenericBound::Outlives ? + }, + } + } + } + + fn add_implied_bound_spans(&mut self, long_lft_ident: &Ident, outlived_lft_ident: &Ident) { + if long_lft_ident.name == outlived_lft_ident.name { + // only unequal symbols form a lifetime bound + return; + } + match self + .implied_bounds_spans + .entry(BoundLftPair::new(long_lft_ident.name, outlived_lft_ident.name)) + { + Entry::Vacant(new_entry) => { + // in nested references the outlived lifetime occurs first + new_entry.insert((outlived_lft_ident.span, long_lft_ident.span)); + }, + Entry::Occupied(mut prev_entry) => { + // keep the first occurrence of the nested reference, + // the insertion order here depends on the recursion order. + let prev_spans = prev_entry.get_mut(); + if (outlived_lft_ident.span < prev_spans.0) + || (outlived_lft_ident.span == prev_spans.0 && (long_lft_ident.span < prev_spans.1)) + { + *prev_spans = (outlived_lft_ident.span, long_lft_ident.span); + } + }, + } + } + + fn report_lints(self, cx: &EarlyContext<'_>) { + let bound_implied_here_note = "this lifetimes bound is implied here:"; + + for (implied_bound, (outlived_lft_span, long_lft_span)) in &self.implied_bounds_spans { + if !self.declared_bounds_spans.contains_key(implied_bound) { + let declaration = implied_bound.as_bound_declaration(); + let msg_missing = format!("missing lifetimes bound declaration: {declaration}"); + if let Some(long_lft_decl_span) = self.declared_lifetimes_spans.get(&implied_bound.long_lft_sym) { + let nested_ref_span = spans_merge(*outlived_lft_span, *long_lft_span); + span_lint_and_fix_sugg_and_note_cause( + cx, + EXPLICIT_LIFETIMES_BOUND, + *long_lft_decl_span, + &msg_missing, + "try", + declaration, + nested_ref_span, + bound_implied_here_note, + ); + } else { + // unreachable!(); collected only bounds on declared lifetimes + span_lint(cx, EXPLICIT_LIFETIMES_BOUND, self.generics_span, msg_missing); + } + } + } + + for (declared_bound, decl_span) in self.declared_bounds_spans { + if let Some((outlived_lft_span, long_lft_span)) = self.implied_bounds_spans.get(&declared_bound) { + let nested_ref_span = spans_merge(*outlived_lft_span, *long_lft_span); + span_lint_and_note( + cx, + IMPLICIT_LIFETIMES_BOUND, + decl_span, + format!( + // only remove the these lifetime bounds after the compiler is fixed + "declared lifetimes bound: {} is redundant, but do not remove it", + declared_bound.as_bound_declaration(), + ), + Some(nested_ref_span), + bound_implied_here_note, + ); + } + } + } +} + +fn spans_merge(span1: Span, span2: Span) -> Span { + Span::new( + span1.lo().min(span2.lo()), + span1.hi().max(span2.hi()), + span1.ctxt(), + span1.parent(), + ) +} + +/// Combine `span_lint_and_sugg` and `span_lint_and_help`: +/// give a lint error, a suggestion to fix, and a note on the cause of the lint in the code. +#[allow(clippy::too_many_arguments)] +fn span_lint_and_fix_sugg_and_note_cause( + cx: &T, + lint: &'static Lint, + sp: Span, + msg: &str, + fix_help: &str, + sugg: String, + cause_span: Span, + cause_note: &'static str, +) { + span_lint_and_then(cx, lint, sp, msg.to_owned(), |diag| { + diag.span_suggestion(sp, fix_help.to_string(), sugg, Applicability::MachineApplicable); + diag.span_note(cause_span, cause_note); + }); +} diff --git a/tests/ui/lifetimes_bound_nested_ref_expl.fixed b/tests/ui/lifetimes_bound_nested_ref_expl.fixed new file mode 100644 index 000000000000..f57dd692b226 --- /dev/null +++ b/tests/ui/lifetimes_bound_nested_ref_expl.fixed @@ -0,0 +1,69 @@ +#![warn(clippy::explicit_lifetimes_bound)] +use core::mem::MaybeUninit; + +// issue 25860, missing implicit bound +pub fn lifetime_translator_1<'lfta, 'lftb: 'lfta, T>(val_a: &'lfta &'lftb T, _val_b: &'lftb T) -> &'lfta T { + val_a +} + +// helper declarations for issue 84591 +trait Supertrait<'a, 'b> { + fn convert(x: &'a T) -> &'b T; +} + +struct MyStruct; + +impl<'a: 'b, 'b> Supertrait<'a, 'b> for MyStruct { + fn convert(x: &'a T) -> &'b T { + x + } +} + +trait Subtrait<'a, 'b, R>: Supertrait<'a, 'b> {} + +// issue 84591, missing implicit bound: +impl<'a: 'b, 'b> Subtrait<'a, 'b, &'b &'a ()> for MyStruct {} + +// helper declarations for issue 100051 +trait Trait1 { + type Type1; +} + +impl Trait1 for T1 { + type Type1 = (); +} + +trait Extend1<'a, 'b> { + fn extend(self, s: &'a str) -> &'b str; +} + +// issue 100051, missing implicit bound +impl<'a: 'b, 'b> Extend1<'a, 'b> for <&'b &'a () as Trait1>::Type1 { + fn extend(self, s: &'a str) -> &'b str { + s + } +} + +// from the httparse crate, lib.rs: bounds implied by argument and return types are the same +// missing implicit bound: +unsafe fn deinit_slice_mut<'a, 'b: 'a, T>(s: &'a mut &'b mut [T]) -> &'a mut &'b mut [MaybeUninit] { + let s: *mut &mut [T] = s; + let s = s as *mut &mut [MaybeUninit]; + &mut *s +} + +// test case for unnamed references. +// helper declarations: +struct Thing1<'a> { + ref_u8: &'a u8, +} +struct Thing2<'a> { + ref_u16: &'a u16, +} +// missing implicit bound +fn test_unnamed_ref<'a: 'b, 'b>(w1: &'b mut &mut Thing1<'a>, w2: &mut Thing2<'b>) -> &'a u8 { + let _ = w2; + w1.ref_u8 +} + +fn main() {} diff --git a/tests/ui/lifetimes_bound_nested_ref_expl.rs b/tests/ui/lifetimes_bound_nested_ref_expl.rs new file mode 100644 index 000000000000..522fedf4f404 --- /dev/null +++ b/tests/ui/lifetimes_bound_nested_ref_expl.rs @@ -0,0 +1,69 @@ +#![warn(clippy::explicit_lifetimes_bound)] +use core::mem::MaybeUninit; + +// issue 25860, missing implicit bound +pub fn lifetime_translator_1<'lfta, 'lftb, T>(val_a: &'lfta &'lftb T, _val_b: &'lftb T) -> &'lfta T { + val_a +} + +// helper declarations for issue 84591 +trait Supertrait<'a, 'b> { + fn convert(x: &'a T) -> &'b T; +} + +struct MyStruct; + +impl<'a: 'b, 'b> Supertrait<'a, 'b> for MyStruct { + fn convert(x: &'a T) -> &'b T { + x + } +} + +trait Subtrait<'a, 'b, R>: Supertrait<'a, 'b> {} + +// issue 84591, missing implicit bound: +impl<'a, 'b> Subtrait<'a, 'b, &'b &'a ()> for MyStruct {} + +// helper declarations for issue 100051 +trait Trait1 { + type Type1; +} + +impl Trait1 for T1 { + type Type1 = (); +} + +trait Extend1<'a, 'b> { + fn extend(self, s: &'a str) -> &'b str; +} + +// issue 100051, missing implicit bound +impl<'a, 'b> Extend1<'a, 'b> for <&'b &'a () as Trait1>::Type1 { + fn extend(self, s: &'a str) -> &'b str { + s + } +} + +// from the httparse crate, lib.rs: bounds implied by argument and return types are the same +// missing implicit bound: +unsafe fn deinit_slice_mut<'a, 'b, T>(s: &'a mut &'b mut [T]) -> &'a mut &'b mut [MaybeUninit] { + let s: *mut &mut [T] = s; + let s = s as *mut &mut [MaybeUninit]; + &mut *s +} + +// test case for unnamed references. +// helper declarations: +struct Thing1<'a> { + ref_u8: &'a u8, +} +struct Thing2<'a> { + ref_u16: &'a u16, +} +// missing implicit bound +fn test_unnamed_ref<'a, 'b>(w1: &'b mut &mut Thing1<'a>, w2: &mut Thing2<'b>) -> &'a u8 { + let _ = w2; + w1.ref_u8 +} + +fn main() {} diff --git a/tests/ui/lifetimes_bound_nested_ref_expl.stderr b/tests/ui/lifetimes_bound_nested_ref_expl.stderr new file mode 100644 index 000000000000..13ceacd5780f --- /dev/null +++ b/tests/ui/lifetimes_bound_nested_ref_expl.stderr @@ -0,0 +1,64 @@ +error: missing lifetimes bound declaration: 'lftb: 'lfta + --> tests/ui/lifetimes_bound_nested_ref_expl.rs:5:37 + | +LL | pub fn lifetime_translator_1<'lfta, 'lftb, T>(val_a: &'lfta &'lftb T, _val_b: &'lftb T) -> &'lfta T { + | ^^^^^ help: try: `'lftb: 'lfta` + | +note: this lifetimes bound is implied here: + --> tests/ui/lifetimes_bound_nested_ref_expl.rs:5:55 + | +LL | pub fn lifetime_translator_1<'lfta, 'lftb, T>(val_a: &'lfta &'lftb T, _val_b: &'lftb T) -> &'lfta T { + | ^^^^^^^^^^^^ + = note: `-D clippy::explicit-lifetimes-bound` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::explicit_lifetimes_bound)]` + +error: missing lifetimes bound declaration: 'a: 'b + --> tests/ui/lifetimes_bound_nested_ref_expl.rs:25:6 + | +LL | impl<'a, 'b> Subtrait<'a, 'b, &'b &'a ()> for MyStruct {} + | ^^ help: try: `'a: 'b` + | +note: this lifetimes bound is implied here: + --> tests/ui/lifetimes_bound_nested_ref_expl.rs:25:32 + | +LL | impl<'a, 'b> Subtrait<'a, 'b, &'b &'a ()> for MyStruct {} + | ^^^^^^ + +error: missing lifetimes bound declaration: 'a: 'b + --> tests/ui/lifetimes_bound_nested_ref_expl.rs:41:6 + | +LL | impl<'a, 'b> Extend1<'a, 'b> for <&'b &'a () as Trait1>::Type1 { + | ^^ help: try: `'a: 'b` + | +note: this lifetimes bound is implied here: + --> tests/ui/lifetimes_bound_nested_ref_expl.rs:41:36 + | +LL | impl<'a, 'b> Extend1<'a, 'b> for <&'b &'a () as Trait1>::Type1 { + | ^^^^^^ + +error: missing lifetimes bound declaration: 'b: 'a + --> tests/ui/lifetimes_bound_nested_ref_expl.rs:49:32 + | +LL | unsafe fn deinit_slice_mut<'a, 'b, T>(s: &'a mut &'b mut [T]) -> &'a mut &'b mut [MaybeUninit] { + | ^^ help: try: `'b: 'a` + | +note: this lifetimes bound is implied here: + --> tests/ui/lifetimes_bound_nested_ref_expl.rs:49:43 + | +LL | unsafe fn deinit_slice_mut<'a, 'b, T>(s: &'a mut &'b mut [T]) -> &'a mut &'b mut [MaybeUninit] { + | ^^^^^^^^^^ + +error: missing lifetimes bound declaration: 'a: 'b + --> tests/ui/lifetimes_bound_nested_ref_expl.rs:64:21 + | +LL | fn test_unnamed_ref<'a, 'b>(w1: &'b mut &mut Thing1<'a>, w2: &mut Thing2<'b>) -> &'a u8 { + | ^^ help: try: `'a: 'b` + | +note: this lifetimes bound is implied here: + --> tests/ui/lifetimes_bound_nested_ref_expl.rs:64:34 + | +LL | fn test_unnamed_ref<'a, 'b>(w1: &'b mut &mut Thing1<'a>, w2: &mut Thing2<'b>) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/lifetimes_bound_nested_ref_impl.rs b/tests/ui/lifetimes_bound_nested_ref_impl.rs new file mode 100644 index 000000000000..222ccd6a8b22 --- /dev/null +++ b/tests/ui/lifetimes_bound_nested_ref_impl.rs @@ -0,0 +1,71 @@ +// This was started as a copy from the fixed output of lifetimes_bound_nested_ref_expl.fixed +// Adapted: the lint name and the code comments. +#![warn(clippy::implicit_lifetimes_bound)] +use core::mem::MaybeUninit; + +// issue 25860, with declared bound +pub fn lifetime_translator_1<'lfta, 'lftb: 'lfta, T>(val_a: &'lfta &'lftb T, _val_b: &'lftb T) -> &'lfta T { + val_a +} + +// helper declarations for issue 84591 +trait Supertrait<'a, 'b> { + fn convert(x: &'a T) -> &'b T; +} + +struct MyStruct; + +impl<'a: 'b, 'b> Supertrait<'a, 'b> for MyStruct { + fn convert(x: &'a T) -> &'b T { + x + } +} + +trait Subtrait<'a, 'b, R>: Supertrait<'a, 'b> {} + +// issue 84591, with declared bound: +impl<'a: 'b, 'b> Subtrait<'a, 'b, &'b &'a ()> for MyStruct {} + +// helper declarations for issue 100051 +trait Trait1 { + type Type1; +} + +impl Trait1 for T1 { + type Type1 = (); +} + +trait Extend1<'a, 'b> { + fn extend(self, s: &'a str) -> &'b str; +} + +// issue 100051, with declared bound +impl<'a: 'b, 'b> Extend1<'a, 'b> for <&'b &'a () as Trait1>::Type1 { + fn extend(self, s: &'a str) -> &'b str { + s + } +} + +// from the httparse crate, lib.rs: bounds implied by argument and return types are the same +// with declared bound: +unsafe fn deinit_slice_mut<'a, 'b: 'a, T>(s: &'a mut &'b mut [T]) -> &'a mut &'b mut [MaybeUninit] { + let s: *mut &mut [T] = s; + let s = s as *mut &mut [MaybeUninit]; + &mut *s +} + +// test case for unnamed references. +// helper declarations: +struct Thing1<'a> { + ref_u8: &'a u8, +} +struct Thing2<'a> { + ref_u16: &'a u16, +} +// with declared bound +fn test_unnamed_ref<'a: 'b, 'b>(w1: &'b mut &mut Thing1<'a>, w2: &mut Thing2<'b>) -> &'a u8 { + let _ = w2; + w1.ref_u8 +} + +fn main() {} diff --git a/tests/ui/lifetimes_bound_nested_ref_impl.stderr b/tests/ui/lifetimes_bound_nested_ref_impl.stderr new file mode 100644 index 000000000000..8a923701cb78 --- /dev/null +++ b/tests/ui/lifetimes_bound_nested_ref_impl.stderr @@ -0,0 +1,64 @@ +error: declared lifetimes bound: 'lftb: 'lfta is redundant, but do not remove it + --> tests/ui/lifetimes_bound_nested_ref_impl.rs:7:42 + | +LL | pub fn lifetime_translator_1<'lfta, 'lftb: 'lfta, T>(val_a: &'lfta &'lftb T, _val_b: &'lftb T) -> &'lfta T { + | ^^^^^^^ + | +note: this lifetimes bound is implied here: + --> tests/ui/lifetimes_bound_nested_ref_impl.rs:7:62 + | +LL | pub fn lifetime_translator_1<'lfta, 'lftb: 'lfta, T>(val_a: &'lfta &'lftb T, _val_b: &'lftb T) -> &'lfta T { + | ^^^^^^^^^^^^ + = note: `-D clippy::implicit-lifetimes-bound` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::implicit_lifetimes_bound)]` + +error: declared lifetimes bound: 'a: 'b is redundant, but do not remove it + --> tests/ui/lifetimes_bound_nested_ref_impl.rs:27:8 + | +LL | impl<'a: 'b, 'b> Subtrait<'a, 'b, &'b &'a ()> for MyStruct {} + | ^^^^ + | +note: this lifetimes bound is implied here: + --> tests/ui/lifetimes_bound_nested_ref_impl.rs:27:36 + | +LL | impl<'a: 'b, 'b> Subtrait<'a, 'b, &'b &'a ()> for MyStruct {} + | ^^^^^^ + +error: declared lifetimes bound: 'a: 'b is redundant, but do not remove it + --> tests/ui/lifetimes_bound_nested_ref_impl.rs:43:8 + | +LL | impl<'a: 'b, 'b> Extend1<'a, 'b> for <&'b &'a () as Trait1>::Type1 { + | ^^^^ + | +note: this lifetimes bound is implied here: + --> tests/ui/lifetimes_bound_nested_ref_impl.rs:43:40 + | +LL | impl<'a: 'b, 'b> Extend1<'a, 'b> for <&'b &'a () as Trait1>::Type1 { + | ^^^^^^ + +error: declared lifetimes bound: 'b: 'a is redundant, but do not remove it + --> tests/ui/lifetimes_bound_nested_ref_impl.rs:51:34 + | +LL | unsafe fn deinit_slice_mut<'a, 'b: 'a, T>(s: &'a mut &'b mut [T]) -> &'a mut &'b mut [MaybeUninit] { + | ^^^^ + | +note: this lifetimes bound is implied here: + --> tests/ui/lifetimes_bound_nested_ref_impl.rs:51:47 + | +LL | unsafe fn deinit_slice_mut<'a, 'b: 'a, T>(s: &'a mut &'b mut [T]) -> &'a mut &'b mut [MaybeUninit] { + | ^^^^^^^^^^ + +error: declared lifetimes bound: 'a: 'b is redundant, but do not remove it + --> tests/ui/lifetimes_bound_nested_ref_impl.rs:66:23 + | +LL | fn test_unnamed_ref<'a: 'b, 'b>(w1: &'b mut &mut Thing1<'a>, w2: &mut Thing2<'b>) -> &'a u8 { + | ^^^^ + | +note: this lifetimes bound is implied here: + --> tests/ui/lifetimes_bound_nested_ref_impl.rs:66:38 + | +LL | fn test_unnamed_ref<'a: 'b, 'b>(w1: &'b mut &mut Thing1<'a>, w2: &mut Thing2<'b>) -> &'a u8 { + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + From e37d681e8215a61b599fe9019256c0e1912b1e0d Mon Sep 17 00:00:00 2001 From: Ype Kingma Date: Mon, 8 Jul 2024 20:33:09 +0200 Subject: [PATCH 02/13] Typo in comment --- clippy_lints/src/lifetimes_bound_nested_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clippy_lints/src/lifetimes_bound_nested_ref.rs b/clippy_lints/src/lifetimes_bound_nested_ref.rs index fd91ed745f3e..8697a6b50c66 100644 --- a/clippy_lints/src/lifetimes_bound_nested_ref.rs +++ b/clippy_lints/src/lifetimes_bound_nested_ref.rs @@ -499,7 +499,7 @@ impl ImpliedBoundsLinter { IMPLICIT_LIFETIMES_BOUND, decl_span, format!( - // only remove the these lifetime bounds after the compiler is fixed + // only remove these lifetime bounds after the compiler is fixed "declared lifetimes bound: {} is redundant, but do not remove it", declared_bound.as_bound_declaration(), ), From 3d63aca5729bc3e06a892ec348c7c89df8b457c4 Mon Sep 17 00:00:00 2001 From: Ype Kingma Date: Mon, 8 Jul 2024 21:07:19 +0200 Subject: [PATCH 03/13] Move unused capturing args from TyKind::ImplTrait() to GenericBound::Use(). Change the comment on GenericBound::Outlives(). --- clippy_lints/src/lifetimes_bound_nested_ref.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/clippy_lints/src/lifetimes_bound_nested_ref.rs b/clippy_lints/src/lifetimes_bound_nested_ref.rs index 8697a6b50c66..faea54c51713 100644 --- a/clippy_lints/src/lifetimes_bound_nested_ref.rs +++ b/clippy_lints/src/lifetimes_bound_nested_ref.rs @@ -376,7 +376,7 @@ impl ImpliedBoundsLinter { // dyn, not needed to detect reported issues self.collect_nested_ref_bounds_gbs(generic_bounds, opt_outlived_lft_ident); }, - TK::ImplTrait(_node_id, generic_bounds, _opt_capturing_args_and_span) => { + TK::ImplTrait(_node_id, generic_bounds) => { // impl, not needed to detect reported issues self.collect_nested_ref_bounds_gbs(generic_bounds, opt_outlived_lft_ident); }, @@ -433,8 +433,11 @@ impl ImpliedBoundsLinter { } }, GB::Outlives(_lifetime) => { - // CHECKME: what is the meaning of GenericBound::Outlives ? + // CHECKME: is this a nested reference bound that can be collected ? }, + GB::Use(_precise_capturing_args, _span) => { + // CHECKME: how to treat the lifetime in a PreciseCapturingArg ? + } } } } From b806bdabf2361d7b54658e629bace83bf8ade73a Mon Sep 17 00:00:00 2001 From: Ype Kingma Date: Tue, 9 Jul 2024 18:28:30 +0200 Subject: [PATCH 04/13] Add some code docs, improve a comment, minor reformat --- clippy_lints/src/lifetimes_bound_nested_ref.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/lifetimes_bound_nested_ref.rs b/clippy_lints/src/lifetimes_bound_nested_ref.rs index faea54c51713..2e8731800c40 100644 --- a/clippy_lints/src/lifetimes_bound_nested_ref.rs +++ b/clippy_lints/src/lifetimes_bound_nested_ref.rs @@ -148,7 +148,10 @@ impl_lint_pass!(LifetimesBoundNestedRef => [ ]); impl EarlyLintPass for LifetimesBoundNestedRef { - /// For issue 25860 + /// For issue 25860: from function arguments and return values, + /// get declared lifetimes and declared lifetime bounds, + /// and compare these with lifetime bounds implied by nested references + /// in the function signature. fn check_fn(&mut self, early_context: &EarlyContext<'_>, fn_kind: FnKind<'_>, _fn_span: Span, _node_id: NodeId) { let FnKind::Fn(_fn_ctxt, _ident, fn_sig, _visibility, generics, _opt_block) = fn_kind else { return; @@ -167,7 +170,11 @@ impl EarlyLintPass for LifetimesBoundNestedRef { linter.report_lints(early_context); } - /// For issues 84591 and 100051 + /// For issues 84591 and 100051: + /// Ignore the supertrait in issue 84591, just check the trait implementation declaration. + /// For issue 100051, check the trait implementation declaration, and also check the projection + /// impl ... for ... . For these, get declared lifetimes and declared lifetime bounds, + /// and compare these with lifetime bounds implied by nested references. fn check_item_post(&mut self, early_context: &EarlyContext<'_>, item: &Item) { let ItemKind::Impl(box_impl) = &item.kind else { return; @@ -188,6 +195,8 @@ impl EarlyLintPass for LifetimesBoundNestedRef { } } +/// A lifetime bound between a pair of lifetime symbols, +/// e.g. 'long: 'outlived. #[derive(Debug)] struct BoundLftPair { long_lft_sym: Symbol, @@ -433,11 +442,11 @@ impl ImpliedBoundsLinter { } }, GB::Outlives(_lifetime) => { - // CHECKME: is this a nested reference bound that can be collected ? + // CHECKME: should this coincide with already collected declared bounds? }, GB::Use(_precise_capturing_args, _span) => { // CHECKME: how to treat the lifetime in a PreciseCapturingArg ? - } + }, } } } From 8bd90c7845820243e9b20ada1c7f78770cc5d2c3 Mon Sep 17 00:00:00 2001 From: Ype Kingma Date: Tue, 9 Jul 2024 18:29:00 +0200 Subject: [PATCH 05/13] Use derived lexicographic ordering for BoundLftPair --- .../src/lifetimes_bound_nested_ref.rs | 27 +------------------ 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/clippy_lints/src/lifetimes_bound_nested_ref.rs b/clippy_lints/src/lifetimes_bound_nested_ref.rs index 2e8731800c40..386e8a528bd8 100644 --- a/clippy_lints/src/lifetimes_bound_nested_ref.rs +++ b/clippy_lints/src/lifetimes_bound_nested_ref.rs @@ -19,7 +19,6 @@ /// There is also a reverse lint that suggest to remove lifetime bounds /// that are implied by nested references. This reverse lint is intended to be used only /// when the compiler has been fixed to handle these lifetime bounds correctly. -use std::cmp::Ordering; use std::collections::btree_map::Entry; use std::collections::BTreeMap; @@ -197,7 +196,7 @@ impl EarlyLintPass for LifetimesBoundNestedRef { /// A lifetime bound between a pair of lifetime symbols, /// e.g. 'long: 'outlived. -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] // use lexicographic ordering struct BoundLftPair { long_lft_sym: Symbol, outlived_lft_sym: Symbol, @@ -216,30 +215,6 @@ impl BoundLftPair { } } -impl PartialEq for BoundLftPair { - fn eq(&self, other: &Self) -> bool { - self.long_lft_sym.eq(&other.long_lft_sym) && self.outlived_lft_sym.eq(&other.outlived_lft_sym) - } -} - -impl Eq for BoundLftPair {} - -impl PartialOrd for BoundLftPair { - fn partial_cmp(&self, other: &BoundLftPair) -> Option { - Some(self.cmp(other)) - } -} - -impl Ord for BoundLftPair { - fn cmp(&self, other: &Self) -> Ordering { - match self.long_lft_sym.cmp(&other.long_lft_sym) { - Ordering::Less => Ordering::Less, - Ordering::Greater => Ordering::Greater, - Ordering::Equal => self.outlived_lft_sym.cmp(&other.outlived_lft_sym), - } - } -} - fn get_declared_lifetimes_spans(generics: &Generics) -> FxHashMap { generics .params From 96e2592ff7b572c88199a940603e28b5edabeff2 Mon Sep 17 00:00:00 2001 From: Ype Kingma Date: Tue, 9 Jul 2024 18:33:31 +0200 Subject: [PATCH 06/13] Rename a function to follow api guidelines --- clippy_lints/src/lifetimes_bound_nested_ref.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/lifetimes_bound_nested_ref.rs b/clippy_lints/src/lifetimes_bound_nested_ref.rs index 386e8a528bd8..393efc144ee5 100644 --- a/clippy_lints/src/lifetimes_bound_nested_ref.rs +++ b/clippy_lints/src/lifetimes_bound_nested_ref.rs @@ -210,7 +210,7 @@ impl BoundLftPair { } } - fn as_bound_declaration(&self) -> String { + fn to_bound_declaration(&self) -> String { format!("{}: {}", self.long_lft_sym, self.outlived_lft_sym) } } @@ -457,7 +457,7 @@ impl ImpliedBoundsLinter { for (implied_bound, (outlived_lft_span, long_lft_span)) in &self.implied_bounds_spans { if !self.declared_bounds_spans.contains_key(implied_bound) { - let declaration = implied_bound.as_bound_declaration(); + let declaration = implied_bound.to_bound_declaration(); let msg_missing = format!("missing lifetimes bound declaration: {declaration}"); if let Some(long_lft_decl_span) = self.declared_lifetimes_spans.get(&implied_bound.long_lft_sym) { let nested_ref_span = spans_merge(*outlived_lft_span, *long_lft_span); @@ -488,7 +488,7 @@ impl ImpliedBoundsLinter { format!( // only remove these lifetime bounds after the compiler is fixed "declared lifetimes bound: {} is redundant, but do not remove it", - declared_bound.as_bound_declaration(), + declared_bound.to_bound_declaration(), ), Some(nested_ref_span), bound_implied_here_note, From aff676c6d077c6afe41cc2989ce25457b2e33a57 Mon Sep 17 00:00:00 2001 From: Ype Kingma Date: Wed, 10 Jul 2024 10:11:00 +0200 Subject: [PATCH 07/13] More code docs, remove unused generics_span --- .../src/lifetimes_bound_nested_ref.rs | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/clippy_lints/src/lifetimes_bound_nested_ref.rs b/clippy_lints/src/lifetimes_bound_nested_ref.rs index 393efc144ee5..d3aed9306799 100644 --- a/clippy_lints/src/lifetimes_bound_nested_ref.rs +++ b/clippy_lints/src/lifetimes_bound_nested_ref.rs @@ -22,7 +22,7 @@ use std::collections::btree_map::Entry; use std::collections::BTreeMap; -use clippy_utils::diagnostics::{span_lint, span_lint_and_note, span_lint_and_then}; +use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; use rustc_ast::visit::FnKind; use rustc_ast::{ AngleBracketedArg, FnRetTy, GenericArg, GenericArgs, GenericBound, GenericParamKind, Generics, Item, ItemKind, @@ -210,11 +210,13 @@ impl BoundLftPair { } } - fn to_bound_declaration(&self) -> String { + /// A declarion for the lifetimes bound + fn to_declaration(&self) -> String { format!("{}: {}", self.long_lft_sym, self.outlived_lft_sym) } } +/// From a [Generics] provide an [FxHashMap] of the declared lifetime symbols to their spans. fn get_declared_lifetimes_spans(generics: &Generics) -> FxHashMap { generics .params @@ -229,6 +231,8 @@ fn get_declared_lifetimes_spans(generics: &Generics) -> FxHashMap .collect() } +/// From a [Generics] provide a [BTreeMap] of the declared lifetime bounds to the spans of the +/// declarations. fn get_declared_bounds_spans(generics: &Generics) -> BTreeMap { let mut declared_bounds = BTreeMap::new(); generics.params.iter().for_each(|gp| { @@ -261,7 +265,6 @@ fn get_declared_bounds_spans(generics: &Generics) -> BTreeMap, - generics_span: Span, declared_bounds_spans: BTreeMap, implied_bounds_spans: BTreeMap, } @@ -271,7 +274,6 @@ impl ImpliedBoundsLinter { ImpliedBoundsLinter { declared_lifetimes_spans, declared_bounds_spans: get_declared_bounds_spans(generics), - generics_span: generics.span, implied_bounds_spans: BTreeMap::new(), } } @@ -457,8 +459,8 @@ impl ImpliedBoundsLinter { for (implied_bound, (outlived_lft_span, long_lft_span)) in &self.implied_bounds_spans { if !self.declared_bounds_spans.contains_key(implied_bound) { - let declaration = implied_bound.to_bound_declaration(); - let msg_missing = format!("missing lifetimes bound declaration: {declaration}"); + let impl_bound_decl = implied_bound.to_declaration(); + let msg_missing = format!("missing lifetimes bound declaration: {impl_bound_decl}"); if let Some(long_lft_decl_span) = self.declared_lifetimes_spans.get(&implied_bound.long_lft_sym) { let nested_ref_span = spans_merge(*outlived_lft_span, *long_lft_span); span_lint_and_fix_sugg_and_note_cause( @@ -467,13 +469,10 @@ impl ImpliedBoundsLinter { *long_lft_decl_span, &msg_missing, "try", - declaration, + impl_bound_decl, nested_ref_span, bound_implied_here_note, ); - } else { - // unreachable!(); collected only bounds on declared lifetimes - span_lint(cx, EXPLICIT_LIFETIMES_BOUND, self.generics_span, msg_missing); } } } @@ -488,7 +487,7 @@ impl ImpliedBoundsLinter { format!( // only remove these lifetime bounds after the compiler is fixed "declared lifetimes bound: {} is redundant, but do not remove it", - declared_bound.to_bound_declaration(), + declared_bound.to_declaration(), ), Some(nested_ref_span), bound_implied_here_note, From ad8213f8378cac920289e8d5535b6ed021011e7e Mon Sep 17 00:00:00 2001 From: Ype Kingma Date: Wed, 10 Jul 2024 11:06:40 +0200 Subject: [PATCH 08/13] Some renames and added code docs --- .../src/lifetimes_bound_nested_ref.rs | 82 +++++++++++-------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/clippy_lints/src/lifetimes_bound_nested_ref.rs b/clippy_lints/src/lifetimes_bound_nested_ref.rs index d3aed9306799..dc34dfd4128e 100644 --- a/clippy_lints/src/lifetimes_bound_nested_ref.rs +++ b/clippy_lints/src/lifetimes_bound_nested_ref.rs @@ -170,10 +170,12 @@ impl EarlyLintPass for LifetimesBoundNestedRef { } /// For issues 84591 and 100051: - /// Ignore the supertrait in issue 84591, just check the trait implementation declaration. - /// For issue 100051, check the trait implementation declaration, and also check the projection - /// impl ... for ... . For these, get declared lifetimes and declared lifetime bounds, - /// and compare these with lifetime bounds implied by nested references. + /// Get declared lifetimes and declared lifetime bounds, + /// and compare these with lifetime bounds implied by nested references + /// in a trait implementation declaration. + /// + /// For issue 100051 also check for nested references in the `for` projection: `impl ... for + /// ...` . fn check_item_post(&mut self, early_context: &EarlyContext<'_>, item: &Item) { let ItemKind::Impl(box_impl) = &item.kind else { return; @@ -197,22 +199,19 @@ impl EarlyLintPass for LifetimesBoundNestedRef { /// A lifetime bound between a pair of lifetime symbols, /// e.g. 'long: 'outlived. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] // use lexicographic ordering -struct BoundLftPair { - long_lft_sym: Symbol, - outlived_lft_sym: Symbol, +struct BoundLftSymbolPair { + long_lft: Symbol, + outlived_lft: Symbol, } -impl BoundLftPair { - fn new(long_lft_sym: Symbol, outlived_lft_sym: Symbol) -> Self { - BoundLftPair { - long_lft_sym, - outlived_lft_sym, - } +impl BoundLftSymbolPair { + fn new(long_lft: Symbol, outlived_lft: Symbol) -> Self { + BoundLftSymbolPair { long_lft, outlived_lft } } /// A declarion for the lifetimes bound fn to_declaration(&self) -> String { - format!("{}: {}", self.long_lft_sym, self.outlived_lft_sym) + format!("{}: {}", self.long_lft, self.outlived_lft) } } @@ -233,7 +232,7 @@ fn get_declared_lifetimes_spans(generics: &Generics) -> FxHashMap /// From a [Generics] provide a [BTreeMap] of the declared lifetime bounds to the spans of the /// declarations. -fn get_declared_bounds_spans(generics: &Generics) -> BTreeMap { +fn get_declared_bounds_spans(generics: &Generics) -> BTreeMap { let mut declared_bounds = BTreeMap::new(); generics.params.iter().for_each(|gp| { let long_lft_sym = gp.ident.name; @@ -244,7 +243,10 @@ fn get_declared_bounds_spans(generics: &Generics) -> BTreeMap BTreeMap BTreeMap, - declared_bounds_spans: BTreeMap, - implied_bounds_spans: BTreeMap, + declared_bounds_spans: BTreeMap, + /// Map each implied lifetime bound to the spans of the earliest lifetime identifier pair + /// that implies the bound by a nested reference. + implied_bounds_span_pairs: BTreeMap, } impl ImpliedBoundsLinter { @@ -274,14 +278,17 @@ impl ImpliedBoundsLinter { ImpliedBoundsLinter { declared_lifetimes_spans, declared_bounds_spans: get_declared_bounds_spans(generics), - implied_bounds_spans: BTreeMap::new(), + implied_bounds_span_pairs: BTreeMap::new(), } } + /// Collect implied lifetime bounds span pairs from a complete [Path] fn collect_implied_lifetime_bounds_path(&mut self, path: &Path) { self.collect_nested_ref_bounds_path(path, None); } + /// Collect implied lifetime bounds span pairs from a [Path] + /// that is possibly contained in an outlived lifetime fn collect_nested_ref_bounds_path(&mut self, path: &Path, opt_outlived_lft_ident: Option<&Ident>) { for path_segment in &path.segments { if let Some(generic_args) = &path_segment.args { @@ -294,7 +301,7 @@ impl ImpliedBoundsLinter { if let Some(outlived_lft_ident) = opt_outlived_lft_ident && self.is_declared_lifetime_sym(long_lft.ident.name) { - self.add_implied_bound_spans(&long_lft.ident, outlived_lft_ident); + self.add_implied_bound_span_pair(&long_lft.ident, outlived_lft_ident); } }, GA::Type(p_ty) => { @@ -309,14 +316,17 @@ impl ImpliedBoundsLinter { } } - fn collect_implied_lifetime_bounds(&mut self, ty: &Ty) { - self.collect_nested_ref_bounds(ty, None); - } - fn is_declared_lifetime_sym(&self, lft_sym: Symbol) -> bool { self.declared_lifetimes_spans.contains_key(&lft_sym) } + /// Collect implied lifetime bounds span pairs from a complete [Ty] + fn collect_implied_lifetime_bounds(&mut self, ty: &Ty) { + self.collect_nested_ref_bounds(ty, None); + } + + /// Collect implied lifetime bounds span pairs from a [Ty] + /// that is possibly contained in an outlived lifetime fn collect_nested_ref_bounds(&mut self, outliving_ty: &Ty, opt_outlived_lft_ident: Option<&Ident>) { let mut outliving_tys = vec![outliving_ty]; // stack to avoid recursion while let Some(ty) = outliving_tys.pop() { @@ -329,7 +339,7 @@ impl ImpliedBoundsLinter { && self.is_declared_lifetime_sym(lifetime.ident.name) { if let Some(outlived_lft_ident) = opt_outlived_lft_ident { - self.add_implied_bound_spans(&lifetime.ident, outlived_lft_ident); + self.add_implied_bound_span_pair(&lifetime.ident, outlived_lft_ident); } // recursion for nested references outliving this lifetime self.collect_nested_ref_bounds(referred_to_ty, Some(&lifetime.ident)); @@ -388,6 +398,8 @@ impl ImpliedBoundsLinter { } } + /// Collect implied lifetime bounds span pairs from [GenericBound]s + /// that are possibly contained in an outlived lifetime fn collect_nested_ref_bounds_gbs( &mut self, generic_bounds: &Vec, @@ -404,7 +416,7 @@ impl ImpliedBoundsLinter { if let Some(outlived_lft_ident) = opt_outlived_lft_ident && self.is_declared_lifetime_sym(bgp.ident.name) { - self.add_implied_bound_spans(&bgp.ident, outlived_lft_ident); + self.add_implied_bound_span_pair(&bgp.ident, outlived_lft_ident); } }, GPK::Type { default: opt_p_ty } => { @@ -428,22 +440,22 @@ impl ImpliedBoundsLinter { } } - fn add_implied_bound_spans(&mut self, long_lft_ident: &Ident, outlived_lft_ident: &Ident) { + fn add_implied_bound_span_pair(&mut self, long_lft_ident: &Ident, outlived_lft_ident: &Ident) { if long_lft_ident.name == outlived_lft_ident.name { // only unequal symbols form a lifetime bound return; } match self - .implied_bounds_spans - .entry(BoundLftPair::new(long_lft_ident.name, outlived_lft_ident.name)) + .implied_bounds_span_pairs + .entry(BoundLftSymbolPair::new(long_lft_ident.name, outlived_lft_ident.name)) { Entry::Vacant(new_entry) => { // in nested references the outlived lifetime occurs first new_entry.insert((outlived_lft_ident.span, long_lft_ident.span)); }, Entry::Occupied(mut prev_entry) => { - // keep the first occurrence of the nested reference, - // the insertion order here depends on the recursion order. + // keep the first occurring spans for the nested reference. + // (the actual insertion order here depends on the recursion order) let prev_spans = prev_entry.get_mut(); if (outlived_lft_ident.span < prev_spans.0) || (outlived_lft_ident.span == prev_spans.0 && (long_lft_ident.span < prev_spans.1)) @@ -457,11 +469,11 @@ impl ImpliedBoundsLinter { fn report_lints(self, cx: &EarlyContext<'_>) { let bound_implied_here_note = "this lifetimes bound is implied here:"; - for (implied_bound, (outlived_lft_span, long_lft_span)) in &self.implied_bounds_spans { + for (implied_bound, (outlived_lft_span, long_lft_span)) in &self.implied_bounds_span_pairs { if !self.declared_bounds_spans.contains_key(implied_bound) { let impl_bound_decl = implied_bound.to_declaration(); let msg_missing = format!("missing lifetimes bound declaration: {impl_bound_decl}"); - if let Some(long_lft_decl_span) = self.declared_lifetimes_spans.get(&implied_bound.long_lft_sym) { + if let Some(long_lft_decl_span) = self.declared_lifetimes_spans.get(&implied_bound.long_lft) { let nested_ref_span = spans_merge(*outlived_lft_span, *long_lft_span); span_lint_and_fix_sugg_and_note_cause( cx, @@ -478,7 +490,7 @@ impl ImpliedBoundsLinter { } for (declared_bound, decl_span) in self.declared_bounds_spans { - if let Some((outlived_lft_span, long_lft_span)) = self.implied_bounds_spans.get(&declared_bound) { + if let Some((outlived_lft_span, long_lft_span)) = self.implied_bounds_span_pairs.get(&declared_bound) { let nested_ref_span = spans_merge(*outlived_lft_span, *long_lft_span); span_lint_and_note( cx, From 18bab344cceef74e76a1aaadf99a1758dcbe7ac4 Mon Sep 17 00:00:00 2001 From: Ype Kingma Date: Wed, 10 Jul 2024 16:15:58 +0200 Subject: [PATCH 09/13] Correct docs mentioning impl block to trait impl decl --- .../src/lifetimes_bound_nested_ref.rs | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/clippy_lints/src/lifetimes_bound_nested_ref.rs b/clippy_lints/src/lifetimes_bound_nested_ref.rs index dc34dfd4128e..8603a24992ae 100644 --- a/clippy_lints/src/lifetimes_bound_nested_ref.rs +++ b/clippy_lints/src/lifetimes_bound_nested_ref.rs @@ -16,7 +16,7 @@ /// The lint here suggests to declare such lifetime bounds in the hope that /// the unsoundness is avoided. /// -/// There is also a reverse lint that suggest to remove lifetime bounds +/// There is also a reverse lint that suggests to remove lifetime bounds /// that are implied by nested references. This reverse lint is intended to be used only /// when the compiler has been fixed to handle these lifetime bounds correctly. use std::collections::btree_map::Entry; @@ -40,7 +40,7 @@ use rustc_hash::FxHashMap; declare_clippy_lint! { /// ### What it does /// Checks for nested references with declared generic lifetimes - /// in function arguments and return values and in implementation blocks. + /// in function arguments and return values and in trait implementation declarations. /// Such a nested reference implies a lifetimes bound because the inner reference must /// outlive the outer reference. /// @@ -97,7 +97,7 @@ declare_clippy_lint! { declare_clippy_lint! { /// ### What it does /// Checks for nested references with declared generic lifetimes - /// in function arguments and return values and in implementation blocks. + /// in function arguments and return values and in trait implementation declarations. /// Such a nested reference implies a lifetimes bound because the inner reference must /// outlive the outer reference. /// @@ -147,10 +147,10 @@ impl_lint_pass!(LifetimesBoundNestedRef => [ ]); impl EarlyLintPass for LifetimesBoundNestedRef { - /// For issue 25860: from function arguments and return values, - /// get declared lifetimes and declared lifetime bounds, - /// and compare these with lifetime bounds implied by nested references - /// in the function signature. + /// For issue 25860: from the generic arguments of a function, + /// get the declared lifetimes and the declared lifetime bounds. + /// Compare these with lifetime bounds implied by nested references + /// in the function arguments and return value. fn check_fn(&mut self, early_context: &EarlyContext<'_>, fn_kind: FnKind<'_>, _fn_span: Span, _node_id: NodeId) { let FnKind::Fn(_fn_ctxt, _ident, fn_sig, _visibility, generics, _opt_block) = fn_kind else { return; @@ -170,12 +170,13 @@ impl EarlyLintPass for LifetimesBoundNestedRef { } /// For issues 84591 and 100051: - /// Get declared lifetimes and declared lifetime bounds, - /// and compare these with lifetime bounds implied by nested references - /// in a trait implementation declaration. + /// From the generic arguments of a trait implementation declaration + /// get the declared lifetimes and the declared lifetime bounds. + /// Compare these with lifetime bounds implied by nested references + /// in the trait implementation declaration. /// - /// For issue 100051 also check for nested references in the `for` projection: `impl ... for - /// ...` . + /// For issue 100051 also check for nested references + /// in the `for` projection: `impl ... for ...` . fn check_item_post(&mut self, early_context: &EarlyContext<'_>, item: &Item) { let ItemKind::Impl(box_impl) = &item.kind else { return; @@ -196,7 +197,7 @@ impl EarlyLintPass for LifetimesBoundNestedRef { } } -/// A lifetime bound between a pair of lifetime symbols, +/// A lifetimes bound between a pair of lifetime symbols, /// e.g. 'long: 'outlived. #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] // use lexicographic ordering struct BoundLftSymbolPair { @@ -455,7 +456,7 @@ impl ImpliedBoundsLinter { }, Entry::Occupied(mut prev_entry) => { // keep the first occurring spans for the nested reference. - // (the actual insertion order here depends on the recursion order) + // (the insertion order here depends on the recursion order on the input types.) let prev_spans = prev_entry.get_mut(); if (outlived_lft_ident.span < prev_spans.0) || (outlived_lft_ident.span == prev_spans.0 && (long_lft_ident.span < prev_spans.1)) From b3497a23c5cc4d8808ac48c9c1847b13e74293bd Mon Sep 17 00:00:00 2001 From: Ype Kingma Date: Wed, 10 Jul 2024 17:18:50 +0200 Subject: [PATCH 10/13] Use Span.to() to replace spans_merge() --- clippy_lints/src/lifetimes_bound_nested_ref.rs | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/clippy_lints/src/lifetimes_bound_nested_ref.rs b/clippy_lints/src/lifetimes_bound_nested_ref.rs index 8603a24992ae..18d4e87a2af0 100644 --- a/clippy_lints/src/lifetimes_bound_nested_ref.rs +++ b/clippy_lints/src/lifetimes_bound_nested_ref.rs @@ -240,7 +240,7 @@ fn get_declared_bounds_spans(generics: &Generics) -> BTreeMap Span { - Span::new( - span1.lo().min(span2.lo()), - span1.hi().max(span2.hi()), - span1.ctxt(), - span1.parent(), - ) -} - /// Combine `span_lint_and_sugg` and `span_lint_and_help`: /// give a lint error, a suggestion to fix, and a note on the cause of the lint in the code. #[allow(clippy::too_many_arguments)] From a3b4021bd0d99149f3123f95d78b58fcbdb3ef6c Mon Sep 17 00:00:00 2001 From: Ype Kingma Date: Wed, 10 Jul 2024 17:21:37 +0200 Subject: [PATCH 11/13] Dogfood: add backticks --- clippy_lints/src/lifetimes_bound_nested_ref.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/clippy_lints/src/lifetimes_bound_nested_ref.rs b/clippy_lints/src/lifetimes_bound_nested_ref.rs index 18d4e87a2af0..42e782f7aa42 100644 --- a/clippy_lints/src/lifetimes_bound_nested_ref.rs +++ b/clippy_lints/src/lifetimes_bound_nested_ref.rs @@ -216,7 +216,7 @@ impl BoundLftSymbolPair { } } -/// From a [Generics] provide an [FxHashMap] of the declared lifetime symbols to their spans. +/// From a [`Generics`] provide an [`FxHashMap`] of the declared lifetime symbols to their spans. fn get_declared_lifetimes_spans(generics: &Generics) -> FxHashMap { generics .params @@ -231,7 +231,7 @@ fn get_declared_lifetimes_spans(generics: &Generics) -> FxHashMap .collect() } -/// From a [Generics] provide a [BTreeMap] of the declared lifetime bounds to the spans of the +/// From a [`Generics`] provide a [`BTreeMap`] of the declared lifetime bounds to the spans of the /// declarations. fn get_declared_bounds_spans(generics: &Generics) -> BTreeMap { let mut declared_bounds = BTreeMap::new(); @@ -399,7 +399,7 @@ impl ImpliedBoundsLinter { } } - /// Collect implied lifetime bounds span pairs from [GenericBound]s + /// Collect implied lifetime bounds span pairs from [`GenericBound`]s /// that are possibly contained in an outlived lifetime fn collect_nested_ref_bounds_gbs( &mut self, From 1c65bdab35ce68d0278ea3443bc9480fe80a20d3 Mon Sep 17 00:00:00 2001 From: Ype Kingma Date: Wed, 16 Oct 2024 13:44:14 +0200 Subject: [PATCH 12/13] cargo dev update_lints and fmt, move use stmts to start of source file --- CHANGELOG.md | 4 +-- README.md | 2 +- book/src/README.md | 2 +- clippy_lints/src/declared_lints.rs | 2 +- .../src/lifetimes_bound_nested_ref.rs | 34 +++++++++---------- tests/ui/lifetimes_bound_nested_ref_impl.rs | 2 +- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b41b99bb7cf..cd9b9f9ef4a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5453,7 +5453,7 @@ Released 2018-09-13 [`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods [`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop [`explicit_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_iter_loop -[`explicit_lifetimes_bound_nested_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_lifetimes_bound_nested_ref +[`explicit_lifetimes_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_lifetimes_bound [`explicit_write`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_write [`extend_from_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_from_slice [`extend_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#extend_with_drain @@ -5515,7 +5515,7 @@ Released 2018-09-13 [`impl_trait_in_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#impl_trait_in_params [`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone [`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher -[`implicit_lifetimes_bound_nested_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_lifetimes_bound_nested_ref +[`implicit_lifetimes_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_lifetimes_bound [`implicit_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_return [`implicit_saturating_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_add [`implicit_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_saturating_sub diff --git a/README.md b/README.md index ec76a6dfb08e..1690e2beb16f 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category. diff --git a/book/src/README.md b/book/src/README.md index 7bdfb97c3acf..23527ba896af 100644 --- a/book/src/README.md +++ b/book/src/README.md @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index 38ca64ae505d..8828ea8dc2d1 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -266,8 +266,8 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::let_with_type_underscore::LET_WITH_TYPE_UNDERSCORE_INFO, crate::lifetimes::EXTRA_UNUSED_LIFETIMES_INFO, crate::lifetimes::NEEDLESS_LIFETIMES_INFO, - crate::lifetimes_bound_nested_ref::IMPLICIT_LIFETIMES_BOUND_INFO, crate::lifetimes_bound_nested_ref::EXPLICIT_LIFETIMES_BOUND_INFO, + crate::lifetimes_bound_nested_ref::IMPLICIT_LIFETIMES_BOUND_INFO, crate::lines_filter_map_ok::LINES_FILTER_MAP_OK_INFO, crate::literal_representation::DECIMAL_LITERAL_REPRESENTATION_INFO, crate::literal_representation::INCONSISTENT_DIGIT_GROUPING_INFO, diff --git a/clippy_lints/src/lifetimes_bound_nested_ref.rs b/clippy_lints/src/lifetimes_bound_nested_ref.rs index 42e782f7aa42..93f96f33f33e 100644 --- a/clippy_lints/src/lifetimes_bound_nested_ref.rs +++ b/clippy_lints/src/lifetimes_bound_nested_ref.rs @@ -1,3 +1,20 @@ +use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; +use rustc_ast::visit::FnKind; +use rustc_ast::{ + AngleBracketedArg, FnRetTy, GenericArg, GenericArgs, GenericBound, GenericParamKind, Generics, Item, ItemKind, + NodeId, Path, Ty, TyKind, WherePredicate, +}; +use rustc_errors::Applicability; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; +use rustc_session::impl_lint_pass; +use rustc_span::symbol::Ident; +use rustc_span::{Span, Symbol}; +use std::collections::BTreeMap; +use std::collections::btree_map::Entry; + +extern crate rustc_hash; +use rustc_hash::FxHashMap; + /// Lints to help dealing with unsoundness due to a compiler bug described here: /// . /// @@ -19,23 +36,6 @@ /// There is also a reverse lint that suggests to remove lifetime bounds /// that are implied by nested references. This reverse lint is intended to be used only /// when the compiler has been fixed to handle these lifetime bounds correctly. -use std::collections::btree_map::Entry; -use std::collections::BTreeMap; - -use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then}; -use rustc_ast::visit::FnKind; -use rustc_ast::{ - AngleBracketedArg, FnRetTy, GenericArg, GenericArgs, GenericBound, GenericParamKind, Generics, Item, ItemKind, - NodeId, Path, Ty, TyKind, WherePredicate, -}; -use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; -use rustc_session::impl_lint_pass; -use rustc_span::symbol::Ident; -use rustc_span::{Span, Symbol}; - -extern crate rustc_hash; -use rustc_hash::FxHashMap; declare_clippy_lint! { /// ### What it does diff --git a/tests/ui/lifetimes_bound_nested_ref_impl.rs b/tests/ui/lifetimes_bound_nested_ref_impl.rs index 222ccd6a8b22..3da34736c9ba 100644 --- a/tests/ui/lifetimes_bound_nested_ref_impl.rs +++ b/tests/ui/lifetimes_bound_nested_ref_impl.rs @@ -1,4 +1,4 @@ -// This was started as a copy from the fixed output of lifetimes_bound_nested_ref_expl.fixed +// This was started as a copy from the fixed output of lifetimes_bound_nested_ref_expl.fixed // Adapted: the lint name and the code comments. #![warn(clippy::implicit_lifetimes_bound)] use core::mem::MaybeUninit; From ddf9aa9f93b46e427e6118d31e6863e4ba13c30a Mon Sep 17 00:00:00 2001 From: Ype Kingma Date: Wed, 16 Oct 2024 13:49:28 +0200 Subject: [PATCH 13/13] Another cargo dev fmt --- clippy_lints/src/lifetimes_bound_nested_ref.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/clippy_lints/src/lifetimes_bound_nested_ref.rs b/clippy_lints/src/lifetimes_bound_nested_ref.rs index 93f96f33f33e..4b0072fca3d8 100644 --- a/clippy_lints/src/lifetimes_bound_nested_ref.rs +++ b/clippy_lints/src/lifetimes_bound_nested_ref.rs @@ -11,10 +11,6 @@ use rustc_span::symbol::Ident; use rustc_span::{Span, Symbol}; use std::collections::BTreeMap; use std::collections::btree_map::Entry; - -extern crate rustc_hash; -use rustc_hash::FxHashMap; - /// Lints to help dealing with unsoundness due to a compiler bug described here: /// . /// @@ -36,6 +32,8 @@ use rustc_hash::FxHashMap; /// There is also a reverse lint that suggests to remove lifetime bounds /// that are implied by nested references. This reverse lint is intended to be used only /// when the compiler has been fixed to handle these lifetime bounds correctly. +extern crate rustc_hash; +use rustc_hash::FxHashMap; declare_clippy_lint! { /// ### What it does