| use clippy_utils::diagnostics::span_lint_and_then; | |
| use clippy_utils::is_from_proc_macro; | |
| use rustc_errors::Applicability; | |
| use rustc_lint::{LateLintPass, declare_lint_pass}; | |
| use rustc_middle::ty; | |
| use std::fmt::Write as _; | |
| declare_clippy_lint! { | |
| /// ### What it does | |
| /// Disallow the use of rest patterns for accessible fields | |
| /// | |
| /// ### Why restrict this? | |
| /// It might lead to unhandled fields when the struct changes. | |
| /// | |
| /// ### Example | |
| /// ```no_run | |
| /// struct S { | |
| /// a: u8, | |
| /// b: u8, | |
| /// c: u8, | |
| /// } | |
| /// | |
| /// let s = S { a: 1, b: 2, c: 3 }; | |
| /// | |
| /// let S { a, b, .. } = s; | |
| /// ``` | |
| /// Use instead: | |
| /// ```no_run | |
| /// struct S { | |
| /// a: u8, | |
| /// b: u8, | |
| /// c: u8, | |
| /// } | |
| /// | |
| /// let s = S { a: 1, b: 2, c: 3 }; | |
| /// | |
| /// let S { a, b, c: _ } = s; | |
| /// ``` | |
| #[clippy::version = "1.99.0"] | |
| pub REST_PATTERN_ACCESSIBLE_FIELD, | |
| restriction, | |
| "rest pattern (`..`) used for accessible field" | |
| } | |
| declare_clippy_lint! { | |
| /// ### What it does | |
| /// Disallow the use of rest patterns that don't capture any fields. | |
| /// | |
| /// ### Why restrict this? | |
| /// It might lead to unhandled fields when the struct changes. | |
| /// | |
| /// ### Example | |
| /// ```no_run | |
| /// struct S { | |
| /// a: u8, | |
| /// b: u8, | |
| /// c: u8, | |
| /// } | |
| /// | |
| /// let s = S { a: 1, b: 2, c: 3 }; | |
| /// | |
| /// let S { a, b, c, .. } = s; | |
| /// ``` | |
| /// Use instead: | |
| /// ```no_run | |
| /// struct S { | |
| /// a: u8, | |
| /// b: u8, | |
| /// c: u8, | |
| /// } | |
| /// | |
| /// let s = S { a: 1, b: 2, c: 3 }; | |
| /// | |
| /// let S { a, b, c } = s; | |
| /// ``` | |
| #[clippy::version = "1.99.0"] | |
| pub UNNECESSARY_REST_PATTERN, | |
| restriction, | |
| "unnecessary rest pattern (`..`) in destructuring expression" | |
| } | |
| declare_lint_pass!(RestWhenDestructuringStruct => [ | |
| REST_PATTERN_ACCESSIBLE_FIELD, | |
| UNNECESSARY_REST_PATTERN, | |
| ]); | |
| impl<'tcx> LateLintPass<'tcx> for RestWhenDestructuringStruct { | |
| fn check_pat(&mut self, cx: &rustc_lint::LateContext<'tcx>, pat: &'tcx rustc_hir::Pat<'tcx>) { | |
| if let rustc_hir::PatKind::Struct(path, fields, Some(dotdot)) = pat.kind | |
| && let qty = cx.typeck_results().qpath_res(&path, pat.hir_id) | |
| && let ty = cx.typeck_results().pat_ty(pat) | |
| && let ty::Adt(a, _) = ty.kind() | |
| && let variant = a.variant_of_res(qty) | |
| { | |
| let mut missing_suggestions = String::new(); | |
| let mut needs_dotdot = variant.field_list_has_applicable_non_exhaustive(); | |
| for field in &variant.fields { | |
| if field.vis.is_accessible_from(cx.tcx.parent_module(pat.hir_id), cx.tcx) { | |
| if !fields.iter().any(|x| x.ident.name == field.name) { | |
| if !missing_suggestions.is_empty() { | |
| missing_suggestions.push_str(", "); | |
| } | |
| let _ = write!(missing_suggestions, "{}: _", field.name.as_str()); | |
| } | |
| } else { | |
| needs_dotdot = true; | |
| } | |
| } | |
| // Filter out results from macros | |
| if (missing_suggestions.is_empty() && needs_dotdot) | |
| || pat.span.in_external_macro(cx.tcx.sess.source_map()) | |
| || is_from_proc_macro(cx, pat) | |
| { | |
| return; | |
| } | |
| if !missing_suggestions.is_empty() { | |
| let suggestion_span = if needs_dotdot { | |
| missing_suggestions.push_str(", "); | |
| dotdot.shrink_to_lo() | |
| } else { | |
| dotdot | |
| }; | |
| let message = if fields.is_empty() { | |
| "consider explicitly ignoring fields with wildcard patterns (`x: _`)" | |
| } else { | |
| "consider explicitly ignoring remaining fields with wildcard patterns (`x: _`)" | |
| }; | |
| span_lint_and_then( | |
| cx, | |
| REST_PATTERN_ACCESSIBLE_FIELD, | |
| pat.span, | |
| "struct destructuring with rest (`..`)", | |
| |diag| { | |
| diag.span_suggestion_verbose( | |
| suggestion_span, | |
| message, | |
| &missing_suggestions, | |
| Applicability::MachineApplicable, | |
| ); | |
| }, | |
| ); | |
| } else if !needs_dotdot { | |
| let message = "consider removing the unnecessary rest pattern (`..`)"; | |
| span_lint_and_then( | |
| cx, | |
| UNNECESSARY_REST_PATTERN, | |
| pat.span, | |
| "unnecessary rest pattern (`..`)", | |
| |diag| { | |
| diag.span_suggestion_verbose(dotdot, message, "", Applicability::MachineApplicable); | |
| }, | |
| ); | |
| } | |
| } | |
| } | |
| } |