diff options
| author | Philipp Krones <hello@philkrones.com> | 2023-03-24 14:04:35 +0100 |
|---|---|---|
| committer | Philipp Krones <hello@philkrones.com> | 2023-03-24 14:26:19 +0100 |
| commit | 8df896c076fd993bad58878ee8a6ed29d8e586ba (patch) | |
| tree | c0edd67687a954bb38d66e77dae3dbd0db3909c5 /clippy_lints/src/unnecessary_struct_initialization.rs | |
| parent | 58eb9964cc627470cdd9fdcdef872a45615227fe (diff) | |
| download | rust-8df896c076fd993bad58878ee8a6ed29d8e586ba.tar.gz rust-8df896c076fd993bad58878ee8a6ed29d8e586ba.zip | |
Merge commit 'd5e2a7aca55ed49fc943b7a07a8eba05ab5a0079' into clippyup
Diffstat (limited to 'clippy_lints/src/unnecessary_struct_initialization.rs')
| -rw-r--r-- | clippy_lints/src/unnecessary_struct_initialization.rs | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/clippy_lints/src/unnecessary_struct_initialization.rs b/clippy_lints/src/unnecessary_struct_initialization.rs new file mode 100644 index 00000000000..af0b4b1592f --- /dev/null +++ b/clippy_lints/src/unnecessary_struct_initialization.rs @@ -0,0 +1,84 @@ +use clippy_utils::{diagnostics::span_lint_and_sugg, get_parent_expr, path_to_local, source::snippet, ty::is_copy}; +use rustc_hir::{BindingAnnotation, Expr, ExprKind, Node, PatKind, UnOp}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// ### What it does + /// Checks for initialization of a `struct` by copying a base without setting + /// any field. + /// + /// ### Why is this bad? + /// Readibility suffers from unnecessary struct building. + /// + /// ### Example + /// ```rust + /// struct S { s: String } + /// + /// let a = S { s: String::from("Hello, world!") }; + /// let b = S { ..a }; + /// ``` + /// Use instead: + /// ```rust + /// struct S { s: String } + /// + /// let a = S { s: String::from("Hello, world!") }; + /// let b = a; + /// ``` + #[clippy::version = "1.70.0"] + pub UNNECESSARY_STRUCT_INITIALIZATION, + complexity, + "struct built from a base that can be written mode concisely" +} +declare_lint_pass!(UnnecessaryStruct => [UNNECESSARY_STRUCT_INITIALIZATION]); + +impl LateLintPass<'_> for UnnecessaryStruct { + fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { + if let ExprKind::Struct(_, &[], Some(base)) = expr.kind { + if let Some(parent) = get_parent_expr(cx, expr) && + let parent_ty = cx.typeck_results().expr_ty_adjusted(parent) && + parent_ty.is_any_ptr() + { + if is_copy(cx, cx.typeck_results().expr_ty(expr)) && path_to_local(base).is_some() { + // When the type implements `Copy`, a reference to the new struct works on the + // copy. Using the original would borrow it. + return; + } + + if parent_ty.is_mutable_ptr() && !is_mutable(cx, base) { + // The original can be used in a mutable reference context only if it is mutable. + return; + } + } + + // TODO: do not propose to replace *XX if XX is not Copy + if let ExprKind::Unary(UnOp::Deref, target) = base.kind && + matches!(target.kind, ExprKind::Path(..)) && + !is_copy(cx, cx.typeck_results().expr_ty(expr)) + { + // `*base` cannot be used instead of the struct in the general case if it is not Copy. + return; + } + + span_lint_and_sugg( + cx, + UNNECESSARY_STRUCT_INITIALIZATION, + expr.span, + "unnecessary struct building", + "replace with", + snippet(cx, base.span, "..").into_owned(), + rustc_errors::Applicability::MachineApplicable, + ); + } + } +} + +fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { + if let Some(hir_id) = path_to_local(expr) && + let Node::Pat(pat) = cx.tcx.hir().get(hir_id) + { + matches!(pat.kind, PatKind::Binding(BindingAnnotation::MUT, ..)) + } else { + true + } +} |
