Make inconsistent_struct_constructor
"all fields are shorthand" requirement configurable (#13737)
Fixes #11846. This PR has three commits: - The first commit adds an `initializer-suggestions` configuration to control suggestion applicability when initializers are present. The following are the options: - "none": do not suggest - "maybe-incorrect": suggest, but do not apply suggestions with `--fix` - "machine-applicable": suggest and apply suggestions with `--fix` - The second commit fixes suggestions to handle field attributes (problem [noticed by @samueltardieu](https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1859261645)). - The third commit adds `initializer-suggestions = "machine-applicable"` to Clippy's `clippy.toml` and applies the suggestions. (Nothing seems to break.) --- changelog: make `inconsistent_struct_constructor` "all fields are shorthand" requirement configurable
This commit is contained in:
commit
a8968e5dd8
41 changed files with 450 additions and 97 deletions
|
@ -6252,6 +6252,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
|
||||
|
|
|
@ -582,6 +582,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<u32>,
|
||||
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
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -532,6 +532,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<u32>,
|
||||
/// 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,
|
||||
|
|
|
@ -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(),
|
||||
});
|
||||
|
||||
|
|
|
@ -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),
|
||||
});
|
||||
}
|
||||
|
|
|
@ -797,8 +797,8 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
|
|||
parser.into_offset_iter(),
|
||||
&doc,
|
||||
Fragments {
|
||||
fragments: &fragments,
|
||||
doc: &doc,
|
||||
fragments: &fragments,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Symbol, usize>,
|
||||
) -> 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::<Vec<_>>();
|
||||
|
||||
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::<Vec<_>>();
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -649,7 +649,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)));
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -470,14 +470,14 @@ fn detect_iter_and_into_iters<'tcx: 'a, 'a>(
|
|||
captured_ids: HirIdSet,
|
||||
) -> Option<Vec<IterFunction>> {
|
||||
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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
});
|
||||
},
|
||||
_ => (),
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(&[]),
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
});
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1243,9 +1243,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);
|
||||
|
|
|
@ -32,8 +32,8 @@ impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> {
|
|||
) -> Self {
|
||||
Self {
|
||||
possible_borrower: TransitiveRelation::default(),
|
||||
cx,
|
||||
body,
|
||||
cx,
|
||||
possible_origin,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,8 +94,8 @@ impl ClippyWarning {
|
|||
Some(Self {
|
||||
name,
|
||||
diag,
|
||||
url,
|
||||
krate: krate.to_string(),
|
||||
url,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
lint-inconsistent-struct-field-initializers = true
|
|
@ -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<HirId>,
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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<HirId>,
|
||||
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,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
@ -46,6 +46,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
|
||||
|
@ -134,6 +135,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
|
||||
|
@ -222,6 +224,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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue