use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{any_parent_is_automatically_derived, contains_name, in_macro, match_def_path, paths};
+use clippy_utils::ty::{has_drop, is_copy};
+use clippy_utils::{any_parent_is_automatically_derived, contains_name, get_parent_expr, match_def_path, paths};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability;
use rustc_span::Span;
declare_clippy_lint! {
- /// **What it does:** Checks for literal calls to `Default::default()`.
+ /// ### What it does
+ /// Checks for literal calls to `Default::default()`.
///
- /// **Why is this bad?** It's more clear to the reader to use the name of the type whose default is
+ /// ### Why is this bad?
+ /// It's more clear to the reader to use the name of the type whose default is
/// being gotten than the generic `Default`.
///
- /// **Known problems:** None.
- ///
- /// **Example:**
+ /// ### Example
/// ```rust
/// // Bad
/// let s: String = Default::default();
/// // Good
/// let s = String::default();
/// ```
+ #[clippy::version = "pre 1.29.0"]
pub DEFAULT_TRAIT_ACCESS,
pedantic,
"checks for literal calls to `Default::default()`"
}
declare_clippy_lint! {
- /// **What it does:** Checks for immediate reassignment of fields initialized
+ /// ### What it does
+ /// Checks for immediate reassignment of fields initialized
/// with Default::default().
///
- /// **Why is this bad?**It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).
+ /// ### Why is this bad?
+ ///It's more idiomatic to use the [functional update syntax](https://doc.rust-lang.org/reference/expressions/struct-expr.html#functional-update-syntax).
///
- /// **Known problems:** Assignments to patterns that are of tuple type are not linted.
+ /// ### Known problems
+ /// Assignments to patterns that are of tuple type are not linted.
///
- /// **Example:**
+ /// ### Example
/// Bad:
/// ```
/// # #[derive(Default)]
/// .. Default::default()
/// };
/// ```
+ #[clippy::version = "1.49.0"]
pub FIELD_REASSIGN_WITH_DEFAULT,
style,
"binding initialized with Default should have its fields set in the initializer"
impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
-impl LateLintPass<'_> for Default {
+impl<'tcx> LateLintPass<'tcx> for Default {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! {
- if !in_macro(expr.span);
+ if !expr.span.from_expansion();
// Avoid cases already linted by `field_reassign_with_default`
if !self.reassigned_linted.contains(&expr.span);
if let ExprKind::Call(path, ..) = expr.kind;
if let ExprKind::Path(ref qpath) = path.kind;
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
if match_def_path(cx, def_id, &paths::DEFAULT_TRAIT_METHOD);
+ if !is_update_syntax_base(cx, expr);
// Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
if let QPath::Resolved(None, _path) = qpath;
let expr_ty = cx.typeck_results().expr_ty(expr);
then {
// TODO: Work out a way to put "whatever the imported way of referencing
// this type in this file" rather than a fully-qualified type.
- let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did));
+ let replacement = format!("{}::default()", cx.tcx.def_path_str(def.did()));
span_lint_and_sugg(
cx,
DEFAULT_TRAIT_ACCESS,
}
#[allow(clippy::too_many_lines)]
- fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
+ fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
// start from the `let mut _ = _::default();` and look at all the following
// statements, see if they re-assign the fields of the binding
let stmts_head = match block.stmts {
if let StmtKind::Local(local) = stmt.kind;
if let Some(expr) = local.init;
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
- if !in_macro(expr.span);
+ if !expr.span.from_expansion();
// only take bindings to identifiers
if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind;
// only when assigning `... = Default::default()`
if let Some(adt) = binding_type.ty_adt_def();
if adt.is_struct();
let variant = adt.non_enum_variant();
- if adt.did.is_local() || !variant.is_field_list_non_exhaustive();
+ if adt.did().is_local() || !variant.is_field_list_non_exhaustive();
let module_did = cx.tcx.parent_module(stmt.hir_id).to_def_id();
if variant
.fields
.iter()
.all(|field| field.vis.is_accessible_from(module_did, cx.tcx));
+ let all_fields_are_copy = variant
+ .fields
+ .iter()
+ .all(|field| {
+ is_copy(cx, cx.tcx.type_of(field.did))
+ });
+ if !has_drop(cx, binding_type) || all_fields_are_copy;
then {
(local, variant, ident.name, binding_type, expr.span)
} else {
let ext_with_default = !variant
.fields
.iter()
- .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.ident.name));
+ .all(|field| assigned_fields.iter().any(|(a, _)| a == &field.name));
let field_list = assigned_fields
.into_iter()
if let ty::Adt(adt_def, substs) = binding_type.kind();
if !substs.is_empty();
then {
- let adt_def_ty_name = cx.tcx.item_name(adt_def.did);
+ let adt_def_ty_name = cx.tcx.item_name(adt_def.did());
let generic_args = substs.iter().collect::<Vec<_>>();
let tys_str = generic_args
.iter()
}
}
}
+
+/// Returns whether `expr` is the update syntax base: `Foo { a: 1, .. base }`
+fn is_update_syntax_base<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+ if_chain! {
+ if let Some(parent) = get_parent_expr(cx, expr);
+ if let ExprKind::Struct(_, _, Some(base)) = parent.kind;
+ then {
+ base.hir_id == expr.hir_id
+ } else {
+ false
+ }
+ }
+}