diff --git a/CHANGELOG.md b/CHANGELOG.md index cc966972939a..7f34b58938e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6250,6 +6250,7 @@ Released 2018-09-13 [`future-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#future-size-threshold [`ignore-interior-mutability`]: https://doc.rust-lang.org/clippy/lint_configuration.html#ignore-interior-mutability [`large-error-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#large-error-threshold +[`lint-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#lint-inconsistent-struct-field-initializers [`literal-representation-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#literal-representation-threshold [`matches-for-let-else`]: https://doc.rust-lang.org/clippy/lint_configuration.html#matches-for-let-else [`max-fn-params-bools`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-fn-params-bools diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md index 275d125096e9..f3a6d99afe35 100644 --- a/book/src/lint_configuration.md +++ b/book/src/lint_configuration.md @@ -572,6 +572,33 @@ The maximum size of the `Err`-variant in a `Result` returned from a function * [`result_large_err`](https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err) +## `lint-inconsistent-struct-field-initializers` +Whether to suggest reordering constructor fields when initializers are present. + +Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the +suggested code would compile, it can change semantics if the initializer expressions have side effects. The +following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: + +```rust +struct MyStruct { + vector: Vec, + length: usize +} +fn main() { + let vector = vec![1,2,3]; + MyStruct { length: vector.len(), vector}; +} +``` + +[from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 + +**Default Value:** `false` + +--- +**Affected lints:** +* [`inconsistent_struct_constructor`](https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor) + + ## `literal-representation-threshold` The lower bound for linting decimal literals diff --git a/clippy.toml b/clippy.toml index a7b0cc56ea12..f4789c9d0303 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1,5 +1,7 @@ avoid-breaking-exported-api = false +lint-inconsistent-struct-field-initializers = true + [[disallowed-methods]] path = "rustc_lint::context::LintContext::lint" reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead" diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs index 41b56b45d9ae..de23e2e05d3b 100644 --- a/clippy_config/src/conf.rs +++ b/clippy_config/src/conf.rs @@ -529,6 +529,26 @@ define_Conf! { /// The maximum size of the `Err`-variant in a `Result` returned from a function #[lints(result_large_err)] large_error_threshold: u64 = 128, + /// Whether to suggest reordering constructor fields when initializers are present. + /// + /// Warnings produced by this configuration aren't necessarily fixed by just reordering the fields. Even if the + /// suggested code would compile, it can change semantics if the initializer expressions have side effects. The + /// following example [from rust-clippy#11846] shows how the suggestion can run into borrow check errors: + /// + /// ```rust + /// struct MyStruct { + /// vector: Vec, + /// length: usize + /// } + /// fn main() { + /// let vector = vec![1,2,3]; + /// MyStruct { length: vector.len(), vector}; + /// } + /// ``` + /// + /// [from rust-clippy#11846]: https://github.com/rust-lang/rust-clippy/issues/11846#issuecomment-1820747924 + #[lints(inconsistent_struct_constructor)] + lint_inconsistent_struct_field_initializers: bool = false, /// The lower bound for linting decimal literals #[lints(decimal_literal_representation)] literal_representation_threshold: u64 = 16384, diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs index c66738592820..790dafa811f9 100644 --- a/clippy_dev/src/fmt.rs +++ b/clippy_dev/src/fmt.rs @@ -179,8 +179,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { #[expect(clippy::drain_collect)] fields.push(ClippyConf { name, - lints: lints.drain(..).collect(), attrs: &conf[attrs_start..attrs_end], + lints: lints.drain(..).collect(), field: conf[field_start..i].trim_end(), }); attrs_start = i; @@ -191,8 +191,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { #[expect(clippy::drain_collect)] fields.push(ClippyConf { name, - lints: lints.drain(..).collect(), attrs: &conf[attrs_start..attrs_end], + lints: lints.drain(..).collect(), field: conf[field_start..i].trim_end(), }); attrs_start = i; @@ -220,8 +220,8 @@ fn fmt_conf(check: bool) -> Result<(), Error> { } fields.push(ClippyConf { name, - lints, attrs: &conf[attrs_start..attrs_end], + lints, field: conf[field_start..].trim_end(), }); diff --git a/clippy_lints/src/arbitrary_source_item_ordering.rs b/clippy_lints/src/arbitrary_source_item_ordering.rs index cf33e1444e47..07d48344d80f 100644 --- a/clippy_lints/src/arbitrary_source_item_ordering.rs +++ b/clippy_lints/src/arbitrary_source_item_ordering.rs @@ -428,8 +428,8 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering { // Makes a note of the current item for comparison with the next. cur_t = Some(CurItem { - order: module_level_order, item, + order: module_level_order, name: get_item_name(item), }); } diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs index fd86db21c165..5478a80383a6 100644 --- a/clippy_lints/src/doc/mod.rs +++ b/clippy_lints/src/doc/mod.rs @@ -798,8 +798,8 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet, attrs: &[ parser.into_offset_iter(), &doc, Fragments { - fragments: &fragments, doc: &doc, + fragments: &fragments, }, )) } diff --git a/clippy_lints/src/entry.rs b/clippy_lints/src/entry.rs index 70524e458c78..2bec4f2f99e5 100644 --- a/clippy_lints/src/entry.rs +++ b/clippy_lints/src/entry.rs @@ -678,12 +678,12 @@ fn find_insert_calls<'tcx>( map: contains_expr.map, key: contains_expr.key, ctxt: expr.span.ctxt(), - edits: Vec::new(), - is_map_used: false, allow_insert_closure: true, can_use_entry: true, in_tail_pos: true, is_single_insert: true, + is_map_used: false, + edits: Vec::new(), loops: Vec::new(), locals: HirIdSet::default(), }; diff --git a/clippy_lints/src/implied_bounds_in_impls.rs b/clippy_lints/src/implied_bounds_in_impls.rs index 4427edb752e0..ef272c305d34 100644 --- a/clippy_lints/src/implied_bounds_in_impls.rs +++ b/clippy_lints/src/implied_bounds_in_impls.rs @@ -243,11 +243,11 @@ fn collect_supertrait_bounds<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds && !predicates.is_empty() { Some(ImplTraitBound { + span: bound.span(), predicates, + trait_def_id, args: path.args.map_or([].as_slice(), |p| p.args), constraints: path.args.map_or([].as_slice(), |p| p.constraints), - trait_def_id, - span: bound.span(), }) } else { None diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs index 4fcd2abb7694..39ff3c13bcce 100644 --- a/clippy_lints/src/inconsistent_struct_constructor.rs +++ b/clippy_lints/src/inconsistent_struct_constructor.rs @@ -1,19 +1,21 @@ -use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_config::Conf; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::fulfill_or_allowed; use clippy_utils::source::snippet; use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; -use rustc_hir::{self as hir, ExprKind, StructTailExpr}; +use rustc_hir::{self as hir, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_middle::ty::TyCtxt; +use rustc_session::impl_lint_pass; +use rustc_span::Span; use rustc_span::symbol::Symbol; -use std::fmt::{self, Write as _}; declare_clippy_lint! { /// ### What it does - /// Checks for struct constructors where all fields are shorthand and - /// the order of the field init shorthand in the constructor is inconsistent - /// with the order in the struct definition. + /// Checks for struct constructors where the order of the field + /// init in the constructor is inconsistent with the order in the + /// struct definition. /// /// ### Why is this bad? /// Since the order of fields in a constructor doesn't affect the @@ -59,16 +61,37 @@ declare_clippy_lint! { #[clippy::version = "1.52.0"] pub INCONSISTENT_STRUCT_CONSTRUCTOR, pedantic, - "the order of the field init shorthand is inconsistent with the order in the struct definition" + "the order of the field init is inconsistent with the order in the struct definition" } -declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); +pub struct InconsistentStructConstructor { + lint_inconsistent_struct_field_initializers: bool, +} + +impl InconsistentStructConstructor { + pub fn new(conf: &'static Conf) -> Self { + Self { + lint_inconsistent_struct_field_initializers: conf.lint_inconsistent_struct_field_initializers, + } + } +} + +impl_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRUCTOR]); impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - if let ExprKind::Struct(qpath, fields, base) = expr.kind - && fields.iter().all(|f| f.is_shorthand) - && !expr.span.from_expansion() + let ExprKind::Struct(_, fields, _) = expr.kind else { + return; + }; + let all_fields_are_shorthand = fields.iter().all(|f| f.is_shorthand); + let applicability = if all_fields_are_shorthand { + Applicability::MachineApplicable + } else if self.lint_inconsistent_struct_field_initializers { + Applicability::MaybeIncorrect + } else { + return; + }; + if !expr.span.from_expansion() && let ty = cx.typeck_results().expr_ty(expr) && let Some(adt_def) = ty.ty_adt_def() && adt_def.is_struct() @@ -85,36 +108,24 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { return; } - let mut ordered_fields: Vec<_> = fields.iter().map(|f| f.ident.name).collect(); - ordered_fields.sort_unstable_by_key(|id| def_order_map[id]); - - let mut fields_snippet = String::new(); - let (last_ident, idents) = ordered_fields.split_last().unwrap(); - for ident in idents { - let _: fmt::Result = write!(fields_snippet, "{ident}, "); - } - fields_snippet.push_str(&last_ident.to_string()); - - let base_snippet = if let StructTailExpr::Base(base) = base { - format!(", ..{}", snippet(cx, base.span, "..")) - } else { - String::new() - }; - - let sugg = format!( - "{} {{ {fields_snippet}{base_snippet} }}", - snippet(cx, qpath.span(), ".."), - ); + let span = field_with_attrs_span(cx.tcx, fields.first().unwrap()) + .with_hi(field_with_attrs_span(cx.tcx, fields.last().unwrap()).hi()); if !fulfill_or_allowed(cx, INCONSISTENT_STRUCT_CONSTRUCTOR, Some(ty_hir_id)) { - span_lint_and_sugg( + span_lint_and_then( cx, INCONSISTENT_STRUCT_CONSTRUCTOR, - expr.span, + span, "struct constructor field order is inconsistent with struct definition field order", - "try", - sugg, - Applicability::MachineApplicable, + |diag| { + let msg = if all_fields_are_shorthand { + "try" + } else { + "if the field evaluation order doesn't matter, try" + }; + let sugg = suggestion(cx, fields, &def_order_map); + diag.span_suggestion(span, msg, sugg, applicability); + }, ); } } @@ -135,3 +146,45 @@ fn is_consistent_order<'tcx>(fields: &'tcx [hir::ExprField<'tcx>], def_order_map true } + +fn suggestion<'tcx>( + cx: &LateContext<'_>, + fields: &'tcx [hir::ExprField<'tcx>], + def_order_map: &FxHashMap, +) -> String { + let ws = fields + .windows(2) + .map(|w| { + let w0_span = field_with_attrs_span(cx.tcx, &w[0]); + let w1_span = field_with_attrs_span(cx.tcx, &w[1]); + let span = w0_span.between(w1_span); + snippet(cx, span, " ") + }) + .collect::>(); + + let mut fields = fields.to_vec(); + fields.sort_unstable_by_key(|field| def_order_map[&field.ident.name]); + let field_snippets = fields + .iter() + .map(|field| snippet(cx, field_with_attrs_span(cx.tcx, field), "..")) + .collect::>(); + + assert_eq!(field_snippets.len(), ws.len() + 1); + + let mut sugg = String::new(); + for i in 0..field_snippets.len() { + sugg += &field_snippets[i]; + if i < ws.len() { + sugg += &ws[i]; + } + } + sugg +} + +fn field_with_attrs_span(tcx: TyCtxt<'_>, field: &hir::ExprField<'_>) -> Span { + if let Some(attr) = tcx.hir().attrs(field.hir_id).first() { + field.span.with_lo(attr.span.lo()) + } else { + field.span + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 25713001f217..54724dcb15fd 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -648,7 +648,11 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(implicit_return::ImplicitReturn)); store.register_late_pass(move |_| Box::new(implicit_saturating_sub::ImplicitSaturatingSub::new(conf))); store.register_late_pass(|_| Box::new(default_numeric_fallback::DefaultNumericFallback)); - store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor)); + store.register_late_pass(move |_| { + Box::new(inconsistent_struct_constructor::InconsistentStructConstructor::new( + conf, + )) + }); store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(conf))); diff --git a/clippy_lints/src/loops/infinite_loop.rs b/clippy_lints/src/loops/infinite_loop.rs index 9f543b79bac4..d863f6f9ab6d 100644 --- a/clippy_lints/src/loops/infinite_loop.rs +++ b/clippy_lints/src/loops/infinite_loop.rs @@ -38,8 +38,8 @@ pub(super) fn check<'tcx>( cx, label, inner_labels: label.into_iter().collect(), - is_finite: false, loop_depth: 0, + is_finite: false, }; loop_visitor.visit_block(loop_block); diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs index 312bcb55a953..006addb987f5 100644 --- a/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -220,11 +220,11 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { // `check_stmt_post` on `(Late)LintPass`, which we'd need to detect when we're leaving a macro span let mut vis = BodyVisitor { + macro_unsafe_blocks: Vec::new(), #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning expn_depth: if body.value.span.from_expansion() { 1 } else { 0 }, - macro_unsafe_blocks: Vec::new(), - lint: self, - cx + cx, + lint: self }; vis.visit_body(body); } diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index ea4984f83adb..2780c3f8af5c 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -470,14 +470,14 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>( captured_ids: HirIdSet, ) -> Option> { let mut visitor = IterFunctionVisitor { - uses: Vec::new(), - target: id, - seen_other: false, - cx, - current_mutably_captured_ids: HirIdSet::default(), illegal_mutable_capture_ids: captured_ids, + current_mutably_captured_ids: HirIdSet::default(), + cx, + uses: Vec::new(), hir_id_uses_map: FxHashMap::default(), current_statement_hir_id: None, + seen_other: false, + target: id, }; visitor.visit_block(block); if visitor.seen_other { diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs index c6d4ef5911ee..8a99974394c3 100644 --- a/clippy_lints/src/methods/str_splitn.rs +++ b/clippy_lints/src/methods/str_splitn.rs @@ -297,8 +297,8 @@ fn parse_iter_usage<'tcx>( { Some(IterUsage { kind: IterUsageKind::NextTuple, - span: e.span, unwrap_kind: None, + span: e.span, }) } else { None diff --git a/clippy_lints/src/methods/unnecessary_fold.rs b/clippy_lints/src/methods/unnecessary_fold.rs index b5d8972d7aad..c27d1fb4903b 100644 --- a/clippy_lints/src/methods/unnecessary_fold.rs +++ b/clippy_lints/src/methods/unnecessary_fold.rs @@ -124,30 +124,30 @@ pub(super) fn check( match lit.node { ast::LitKind::Bool(false) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Or, Replacement { + method_name: "any", has_args: true, has_generic_return: false, - method_name: "any", }); }, ast::LitKind::Bool(true) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::And, Replacement { + method_name: "all", has_args: true, has_generic_return: false, - method_name: "all", }); }, ast::LitKind::Int(Pu128(0), _) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Add, Replacement { + method_name: "sum", has_args: false, has_generic_return: needs_turbofish(cx, expr), - method_name: "sum", }); }, ast::LitKind::Int(Pu128(1), _) => { check_fold_with_op(cx, expr, acc, fold_span, hir::BinOpKind::Mul, Replacement { + method_name: "product", has_args: false, has_generic_return: needs_turbofish(cx, expr), - method_name: "product", }); }, _ => (), diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs index c48232f99058..a7cd29bb8e32 100644 --- a/clippy_lints/src/needless_continue.rs +++ b/clippy_lints/src/needless_continue.rs @@ -349,11 +349,11 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) { for (i, stmt) in loop_block.stmts.iter().enumerate() { with_if_expr(stmt, |if_expr, cond, then_block, else_expr| { let data = &LintData { - stmt_idx: i, if_expr, if_cond: cond, if_block: then_block, else_expr, + stmt_idx: i, loop_block, }; if needless_continue_in_else(else_expr, label) { diff --git a/clippy_lints/src/non_expressive_names.rs b/clippy_lints/src/non_expressive_names.rs index 2fee1c72a91b..56c4157d6fe0 100644 --- a/clippy_lints/src/non_expressive_names.rs +++ b/clippy_lints/src/non_expressive_names.rs @@ -270,10 +270,10 @@ impl SimilarNamesNameVisitor<'_, '_, '_> { return; } self.0.names.push(ExistingName { - exemptions: get_exemptions(interned_name).unwrap_or(&[]), interned: ident.name, span: ident.span, len: count, + exemptions: get_exemptions(interned_name).unwrap_or(&[]), }); } diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs index 65ef56fd2112..0eca788c7874 100644 --- a/clippy_lints/src/operators/arithmetic_side_effects.rs +++ b/clippy_lints/src/operators/arithmetic_side_effects.rs @@ -47,6 +47,7 @@ impl ArithmeticSideEffects { Self { allowed_binary, allowed_unary, + const_span: None, disallowed_int_methods: [ sym::saturating_div, sym::wrapping_div, @@ -55,7 +56,6 @@ impl ArithmeticSideEffects { ] .into_iter() .collect(), - const_span: None, expr_span: None, } } diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs index d2529d4d9f85..668f09bbfd58 100644 --- a/clippy_lints/src/pathbuf_init_then_push.rs +++ b/clippy_lints/src/pathbuf_init_then_push.rs @@ -143,11 +143,11 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { self.searcher = Some(PathbufPushSearcher { local_id: id, lhs_is_let: true, - name: name.name, let_ty_span: local.ty.map(|ty| ty.span), - err_span: local.span, init_val: *init_expr, arg: None, + name: name.name, + err_span: local.span, }); } } @@ -165,10 +165,10 @@ impl<'tcx> LateLintPass<'tcx> for PathbufThenPush<'tcx> { local_id: id, lhs_is_let: false, let_ty_span: None, - name: name.ident.name, - err_span: expr.span, init_val: *right, arg: None, + name: name.ident.name, + err_span: expr.span, }); } } diff --git a/clippy_lints/src/significant_drop_tightening.rs b/clippy_lints/src/significant_drop_tightening.rs index c690696aefc1..597bfddecbc5 100644 --- a/clippy_lints/src/significant_drop_tightening.rs +++ b/clippy_lints/src/significant_drop_tightening.rs @@ -282,9 +282,9 @@ impl<'tcx> Visitor<'tcx> for StmtsChecker<'_, '_, '_, '_, 'tcx> { } { let mut apa = AuxParamsAttr { - first_bind_ident: ident, first_block_hir_id: self.ap.curr_block_hir_id, first_block_span: self.ap.curr_block_span, + first_bind_ident: ident, first_method_span: { let expr_or_init = expr_or_init(self.cx, expr); if let hir::ExprKind::MethodCall(_, local_expr, _, span) = expr_or_init.kind { @@ -395,8 +395,8 @@ impl Default for AuxParamsAttr { counter: 0, has_expensive_expr_after_last_attr: false, first_block_hir_id: HirId::INVALID, - first_bind_ident: Ident::empty(), first_block_span: DUMMY_SP, + first_bind_ident: Ident::empty(), first_method_span: DUMMY_SP, first_stmt_span: DUMMY_SP, last_bind_ident: Ident::empty(), diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs index 78f0b7d121c2..ff1168005123 100644 --- a/clippy_lints/src/swap.rs +++ b/clippy_lints/src/swap.rs @@ -384,9 +384,9 @@ impl<'tcx> IndexBinding<'_, 'tcx> { fn is_used_after_swap(&mut self, idx_ident: Ident) -> bool { let mut v = IndexBindingVisitor { - found_used: false, - suggest_span: self.suggest_span, idx: idx_ident, + suggest_span: self.suggest_span, + found_used: false, }; for stmt in self.block.stmts { diff --git a/clippy_lints/src/types/mod.rs b/clippy_lints/src/types/mod.rs index 363aea8be72e..43cce625c641 100644 --- a/clippy_lints/src/types/mod.rs +++ b/clippy_lints/src/types/mod.rs @@ -388,8 +388,8 @@ impl<'tcx> LateLintPass<'tcx> for Types { self.check_fn_decl(cx, decl, CheckTyContext { is_in_trait_impl, - is_exported, in_body: matches!(fn_kind, FnKind::Closure), + is_exported, ..CheckTyContext::default() }); } diff --git a/clippy_lints/src/unused_async.rs b/clippy_lints/src/unused_async.rs index c899b1868a6c..d00bd7f2b3db 100644 --- a/clippy_lints/src/unused_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -120,8 +120,8 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { let mut visitor = AsyncFnVisitor { cx, found_await: false, - async_depth: 0, await_in_async_block: None, + async_depth: 0, }; walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), def_id); if !visitor.found_await { @@ -129,9 +129,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { // The actual linting happens in `check_crate_post`, once we've found all // uses of local async functions that do require asyncness to pass typeck self.unused_async_fns.push(UnusedAsyncFn { - await_in_async_block: visitor.await_in_async_block, - fn_span: span, def_id, + fn_span: span, + await_in_async_block: visitor.await_in_async_block, }); } } diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs index 89bb429e2656..eaa119b045f1 100644 --- a/clippy_lints/src/unwrap.rs +++ b/clippy_lints/src/unwrap.rs @@ -245,9 +245,9 @@ impl<'tcx> UnwrappableVariablesVisitor<'_, 'tcx> { let prev_len = self.unwrappables.len(); for unwrap_info in collect_unwrap_info(self.cx, if_expr, cond, branch, else_branch, true) { let mut delegate = MutationVisitor { - tcx: self.cx.tcx, is_mutated: false, local_id: unwrap_info.local_id, + tcx: self.cx.tcx, }; let vis = ExprUseVisitor::for_clippy(self.cx, cond.hir_id.owner.def_id, &mut delegate); @@ -397,8 +397,8 @@ impl<'tcx> LateLintPass<'tcx> for Unwrap { } let mut v = UnwrappableVariablesVisitor { - cx, unwrappables: Vec::new(), + cx, }; walk_fn(&mut v, kind, decl, body.id(), fn_id); diff --git a/clippy_lints/src/vec_init_then_push.rs b/clippy_lints/src/vec_init_then_push.rs index cbc6885ae5de..d87d554eb074 100644 --- a/clippy_lints/src/vec_init_then_push.rs +++ b/clippy_lints/src/vec_init_then_push.rs @@ -166,8 +166,8 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { local_id: id, init, lhs_is_let: true, - name: name.name, let_ty_span: local.ty.map(|ty| ty.span), + name: name.name, err_span: local.span, found: 0, last_push_expr: init_expr.hir_id, @@ -206,8 +206,8 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush { && name.ident.as_str() == "push" { self.searcher = Some(VecPushSearcher { - found: searcher.found + 1, err_span: searcher.err_span.to(stmt.span), + found: searcher.found + 1, last_push_expr: expr.hir_id, ..searcher }); diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs index a42ddcdae353..31ae002e47d9 100644 --- a/clippy_lints/src/write.rs +++ b/clippy_lints/src/write.rs @@ -248,8 +248,8 @@ impl Write { pub fn new(conf: &'static Conf, format_args: FormatArgsStorage) -> Self { Self { format_args, - allow_print_in_tests: conf.allow_print_in_tests, in_debug_impl: false, + allow_print_in_tests: conf.allow_print_in_tests, } } } diff --git a/clippy_lints/src/zombie_processes.rs b/clippy_lints/src/zombie_processes.rs index a702e0785a96..4df34891a2b1 100644 --- a/clippy_lints/src/zombie_processes.rs +++ b/clippy_lints/src/zombie_processes.rs @@ -300,8 +300,8 @@ fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, cause: Caus }; let mut vis = ExitPointFinder { - cx, state: ExitPointState::WalkUpTo(spawn_expr.hir_id), + cx, }; if let Break(ExitCallFound) = vis.visit_block(block) { // Visitor found an unconditional `exit()` call, so don't lint. diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs index 4e12577b6df6..60be7e4a4d39 100644 --- a/clippy_utils/src/higher.rs +++ b/clippy_utils/src/higher.rs @@ -196,8 +196,8 @@ impl<'hir> IfOrIfLet<'hir> { if let ExprKind::DropTemps(new_cond) = cond.kind { return Some(Self { cond: new_cond, - r#else, then, + r#else, }); } if let ExprKind::Let(..) = cond.kind { diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs index ed52c481de12..dce24d26f2ae 100644 --- a/clippy_utils/src/hir_utils.rs +++ b/clippy_utils/src/hir_utils.rs @@ -788,8 +788,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> { Self { cx, maybe_typeck_results: cx.maybe_typeck_results(), - path_check: PathCheck::default(), s: FxHasher::default(), + path_check: PathCheck::default(), } } diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index d11f1a6c12f5..f4d75e8b2283 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -1232,9 +1232,9 @@ pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<' let mut v = V { cx, - allow_closure: true, loops: Vec::new(), locals: HirIdSet::default(), + allow_closure: true, captures: HirIdMap::default(), }; v.visit_expr(expr); diff --git a/clippy_utils/src/mir/possible_borrower.rs b/clippy_utils/src/mir/possible_borrower.rs index 17e6558a41c4..cf73bae2583b 100644 --- a/clippy_utils/src/mir/possible_borrower.rs +++ b/clippy_utils/src/mir/possible_borrower.rs @@ -32,8 +32,8 @@ impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> { ) -> Self { Self { possible_borrower: TransitiveRelation::default(), - cx, body, + cx, possible_origin, } } diff --git a/lintcheck/src/output.rs b/lintcheck/src/output.rs index e38036315c2c..dcc1ec339ef9 100644 --- a/lintcheck/src/output.rs +++ b/lintcheck/src/output.rs @@ -94,8 +94,8 @@ impl ClippyWarning { Some(Self { name, diag, - url, krate: krate.to_string(), + url, }) } diff --git a/tests/compile-test.rs b/tests/compile-test.rs index b8e0413e97bc..e2e4d92df79f 100644 --- a/tests/compile-test.rs +++ b/tests/compile-test.rs @@ -574,12 +574,12 @@ impl LintMetadata { id_location: None, group: "deprecated", level: "none", - version, docs: format!( "### What it does\n\n\ Nothing. This lint has been deprecated\n\n\ ### Deprecation reason\n\n{reason}.\n", ), + version, applicability: Applicability::Unspecified, } } diff --git a/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml b/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml new file mode 100644 index 000000000000..f43c9d97e825 --- /dev/null +++ b/tests/ui-toml/toml_inconsistent_struct_constructor/clippy.toml @@ -0,0 +1 @@ +lint-inconsistent-struct-field-initializers = true diff --git a/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.fixed b/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.fixed new file mode 100644 index 000000000000..8092e40ff9f1 --- /dev/null +++ b/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.fixed @@ -0,0 +1,79 @@ +#![warn(clippy::inconsistent_struct_constructor)] +#![allow(clippy::redundant_field_names)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::no_effect)] + +#[derive(Default)] +struct Foo { + x: i32, + y: i32, + z: i32, +} + +fn main() { + let x = 1; + let y = 1; + let z = 1; + + Foo { x, y, z: z }; + + Foo { + x, + z: z, + ..Default::default() + }; +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1859261645 +mod field_attributes { + struct HirId; + struct BodyVisitor { + macro_unsafe_blocks: Vec, + expn_depth: u32, + } + fn check_body(condition: bool) { + BodyVisitor { + macro_unsafe_blocks: Vec::new(), + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning + expn_depth: if condition { 1 } else { 0 }, + }; + } +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1874539800 +mod cfgs_between_fields { + #[allow(clippy::non_minimal_cfg)] + fn cfg_all() { + struct S { + a: i32, + b: i32, + #[cfg(all())] + c: i32, + d: i32, + } + let s = S { + a: 3, + b: 2, + #[cfg(all())] + c: 1, + d: 0, + }; + } + + fn cfg_any() { + struct S { + a: i32, + b: i32, + #[cfg(any())] + c: i32, + d: i32, + } + let s = S { + a: 3, + #[cfg(any())] + c: 1, + b: 2, + d: 0, + }; + } +} diff --git a/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs b/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs new file mode 100644 index 000000000000..cd1aff966528 --- /dev/null +++ b/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs @@ -0,0 +1,79 @@ +#![warn(clippy::inconsistent_struct_constructor)] +#![allow(clippy::redundant_field_names)] +#![allow(clippy::unnecessary_operation)] +#![allow(clippy::no_effect)] + +#[derive(Default)] +struct Foo { + x: i32, + y: i32, + z: i32, +} + +fn main() { + let x = 1; + let y = 1; + let z = 1; + + Foo { y, x, z: z }; + + Foo { + z: z, + x, + ..Default::default() + }; +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1859261645 +mod field_attributes { + struct HirId; + struct BodyVisitor { + macro_unsafe_blocks: Vec, + expn_depth: u32, + } + fn check_body(condition: bool) { + BodyVisitor { + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning + expn_depth: if condition { 1 } else { 0 }, + macro_unsafe_blocks: Vec::new(), + }; + } +} + +// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1874539800 +mod cfgs_between_fields { + #[allow(clippy::non_minimal_cfg)] + fn cfg_all() { + struct S { + a: i32, + b: i32, + #[cfg(all())] + c: i32, + d: i32, + } + let s = S { + d: 0, + #[cfg(all())] + c: 1, + b: 2, + a: 3, + }; + } + + fn cfg_any() { + struct S { + a: i32, + b: i32, + #[cfg(any())] + c: i32, + d: i32, + } + let s = S { + d: 0, + #[cfg(any())] + c: 1, + b: 2, + a: 3, + }; + } +} diff --git a/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.stderr b/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.stderr new file mode 100644 index 000000000000..d2533960b84c --- /dev/null +++ b/tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.stderr @@ -0,0 +1,77 @@ +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:18:11 + | +LL | Foo { y, x, z: z }; + | ^^^^^^^^^^ help: if the field evaluation order doesn't matter, try: `x, y, z: z` + | + = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::inconsistent_struct_constructor)]` + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:21:9 + | +LL | / z: z, +LL | | x, + | |_________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ x, +LL ~ z: z, + | + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:36:13 + | +LL | / #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning +LL | | expn_depth: if condition { 1 } else { 0 }, +LL | | macro_unsafe_blocks: Vec::new(), + | |___________________________________________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ macro_unsafe_blocks: Vec::new(), +LL + #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning +LL ~ expn_depth: if condition { 1 } else { 0 }, + | + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:55:13 + | +LL | / d: 0, +LL | | #[cfg(all())] +LL | | c: 1, +LL | | b: 2, +LL | | a: 3, + | |________________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ a: 3, +LL + b: 2, +LL + #[cfg(all())] +LL + c: 1, +LL ~ d: 0, + | + +error: struct constructor field order is inconsistent with struct definition field order + --> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:72:13 + | +LL | / d: 0, +LL | | #[cfg(any())] +LL | | c: 1, +LL | | b: 2, +LL | | a: 3, + | |________________^ + | +help: if the field evaluation order doesn't matter, try + | +LL ~ a: 3, +LL + #[cfg(any())] +LL + c: 1, +LL + b: 2, +LL ~ d: 0, + | + +error: aborting due to 5 previous errors + diff --git a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index 6fa583fc0417..c13dd76a821e 100644 --- a/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -45,6 +45,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect future-size-threshold ignore-interior-mutability large-error-threshold + lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -132,6 +133,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect future-size-threshold ignore-interior-mutability large-error-threshold + lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools @@ -219,6 +221,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni future-size-threshold ignore-interior-mutability large-error-threshold + lint-inconsistent-struct-field-initializers literal-representation-threshold matches-for-let-else max-fn-params-bools diff --git a/tests/ui/inconsistent_struct_constructor.fixed b/tests/ui/inconsistent_struct_constructor.fixed index 4c324587c96f..67bd3e4d2797 100644 --- a/tests/ui/inconsistent_struct_constructor.fixed +++ b/tests/ui/inconsistent_struct_constructor.fixed @@ -60,7 +60,11 @@ mod with_base { let z = 1; // Should lint. - Foo { x, z, ..Default::default() }; + Foo { + x, + z, + ..Default::default() + }; // Should NOT lint because the order is consistent with the definition. Foo { diff --git a/tests/ui/inconsistent_struct_constructor.stderr b/tests/ui/inconsistent_struct_constructor.stderr index 97bb7c789a72..c145eb2a239e 100644 --- a/tests/ui/inconsistent_struct_constructor.stderr +++ b/tests/ui/inconsistent_struct_constructor.stderr @@ -1,21 +1,24 @@ error: struct constructor field order is inconsistent with struct definition field order - --> tests/ui/inconsistent_struct_constructor.rs:36:9 + --> tests/ui/inconsistent_struct_constructor.rs:36:15 | LL | Foo { y, x, z }; - | ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }` + | ^^^^^^^ help: try: `x, y, z` | = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::inconsistent_struct_constructor)]` error: struct constructor field order is inconsistent with struct definition field order - --> tests/ui/inconsistent_struct_constructor.rs:63:9 + --> tests/ui/inconsistent_struct_constructor.rs:64:13 | -LL | / Foo { -LL | | z, +LL | / z, LL | | x, -LL | | ..Default::default() -LL | | }; - | |_________^ help: try: `Foo { x, z, ..Default::default() }` + | |_____________^ + | +help: try + | +LL ~ x, +LL ~ z, + | error: aborting due to 2 previous errors