2021-03-25 13:29:11 -05:00
use clippy_utils ::diagnostics ::{ span_lint_and_note , span_lint_and_sugg } ;
2023-03-24 08:04:35 -05:00
use clippy_utils ::source ::snippet_with_context ;
2021-10-21 06:11:36 -05:00
use clippy_utils ::ty ::{ has_drop , is_copy } ;
2023-09-26 22:56:38 -05:00
use clippy_utils ::{ any_parent_is_automatically_derived , contains_name , get_parent_expr , is_from_proc_macro } ;
2020-11-05 07:29:48 -06:00
use rustc_data_structures ::fx ::FxHashSet ;
use rustc_errors ::Applicability ;
use rustc_hir ::def ::Res ;
use rustc_hir ::{ Block , Expr , ExprKind , PatKind , QPath , Stmt , StmtKind } ;
use rustc_lint ::{ LateContext , LateLintPass } ;
2021-01-02 09:29:43 -06:00
use rustc_middle ::ty ;
2023-01-12 12:48:13 -06:00
use rustc_middle ::ty ::print ::with_forced_trimmed_paths ;
2023-12-01 11:21:58 -06:00
use rustc_session ::impl_lint_pass ;
2020-11-05 07:29:48 -06:00
use rustc_span ::symbol ::{ Ident , Symbol } ;
2023-09-26 22:56:38 -05:00
use rustc_span ::{ sym , Span } ;
2020-11-05 07:29:48 -06:00
declare_clippy_lint! {
2021-07-29 05:16:06 -05:00
/// ### What it does
/// Checks for literal calls to `Default::default()`.
2020-11-05 07:29:48 -06:00
///
2021-07-29 05:16:06 -05:00
/// ### Why is this bad?
2022-06-16 10:39:06 -05:00
/// It's easier for the reader if the name of the type is used, rather than the
/// generic `Default`.
2020-11-05 07:29:48 -06:00
///
2021-07-29 05:16:06 -05:00
/// ### Example
2023-11-02 11:35:56 -05:00
/// ```no_run
2020-11-05 07:29:48 -06:00
/// let s: String = Default::default();
2022-06-16 10:39:06 -05:00
/// ```
2020-11-05 07:29:48 -06:00
///
2022-06-16 10:39:06 -05:00
/// Use instead:
2023-11-02 11:35:56 -05:00
/// ```no_run
2020-11-05 07:29:48 -06:00
/// let s = String::default();
/// ```
2021-12-06 05:33:31 -06:00
#[ clippy::version = " pre 1.29.0 " ]
2020-11-05 07:29:48 -06:00
pub DEFAULT_TRAIT_ACCESS ,
pedantic ,
" checks for literal calls to `Default::default()` "
}
declare_clippy_lint! {
2021-07-29 05:16:06 -05:00
/// ### What it does
/// Checks for immediate reassignment of fields initialized
2020-11-05 07:29:48 -06:00
/// with Default::default().
///
2021-07-29 05:16:06 -05:00
/// ### 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).
2020-11-05 07:29:48 -06:00
///
2021-07-29 05:16:06 -05:00
/// ### Known problems
/// Assignments to patterns that are of tuple type are not linted.
2020-11-05 07:29:48 -06:00
///
2021-07-29 05:16:06 -05:00
/// ### Example
2023-11-02 11:35:56 -05:00
/// ```no_run
2020-11-05 07:29:48 -06:00
/// # #[derive(Default)]
/// # struct A { i: i32 }
/// let mut a: A = Default::default();
/// a.i = 42;
/// ```
2022-06-16 10:39:06 -05:00
///
2020-11-05 07:29:48 -06:00
/// Use instead:
2023-11-02 11:35:56 -05:00
/// ```no_run
2020-11-05 07:29:48 -06:00
/// # #[derive(Default)]
/// # struct A { i: i32 }
/// let a = A {
/// i: 42,
/// .. Default::default()
/// };
/// ```
2021-12-06 05:33:31 -06:00
#[ clippy::version = " 1.49.0 " ]
2020-11-05 07:29:48 -06:00
pub FIELD_REASSIGN_WITH_DEFAULT ,
style ,
" binding initialized with Default should have its fields set in the initializer "
}
#[ derive(Default) ]
pub struct Default {
// Spans linted by `field_reassign_with_default`.
reassigned_linted : FxHashSet < Span > ,
}
impl_lint_pass! ( Default = > [ DEFAULT_TRAIT_ACCESS , FIELD_REASSIGN_WITH_DEFAULT ] ) ;
2022-01-13 06:18:19 -06:00
impl < ' tcx > LateLintPass < ' tcx > for Default {
2020-11-05 07:29:48 -06:00
fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < '_ > ) {
2023-11-16 12:13:24 -06:00
if ! expr . span . from_expansion ( )
2020-11-05 07:29:48 -06:00
// Avoid cases already linted by `field_reassign_with_default`
2023-11-16 12:13:24 -06:00
& & ! self . reassigned_linted . contains ( & expr . span )
& & let ExprKind ::Call ( path , .. ) = expr . kind
& & ! any_parent_is_automatically_derived ( cx . tcx , expr . hir_id )
& & let ExprKind ::Path ( ref qpath ) = path . kind
& & let Some ( def_id ) = cx . qpath_res ( qpath , path . hir_id ) . opt_def_id ( )
& & cx . tcx . is_diagnostic_item ( sym ::default_fn , def_id )
& & ! is_update_syntax_base ( cx , expr )
2020-11-05 07:29:48 -06:00
// Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
2023-11-16 12:13:24 -06:00
& & let QPath ::Resolved ( None , _path ) = qpath
& & let expr_ty = cx . typeck_results ( ) . expr_ty ( expr )
& & let ty ::Adt ( def , .. ) = expr_ty . kind ( )
& & ! is_from_proc_macro ( cx , expr )
{
let replacement = with_forced_trimmed_paths! ( format! ( " {} ::default() " , cx . tcx . def_path_str ( def . did ( ) ) ) ) ;
span_lint_and_sugg (
cx ,
DEFAULT_TRAIT_ACCESS ,
expr . span ,
2024-04-04 12:52:55 -05:00
format! ( " calling ` {replacement} ` is more clear than this expression " ) ,
2023-11-16 12:13:24 -06:00
" try " ,
replacement ,
Applicability ::Unspecified , // First resolve the TODO above
) ;
2020-11-05 07:29:48 -06:00
}
}
2022-05-21 06:24:00 -05:00
#[ expect(clippy::too_many_lines) ]
2022-01-13 06:18:19 -06:00
fn check_block ( & mut self , cx : & LateContext < ' tcx > , block : & Block < ' tcx > ) {
2020-11-05 07:29:48 -06:00
// start from the `let mut _ = _::default();` and look at all the following
// statements, see if they re-assign the fields of the binding
2021-01-02 09:29:43 -06:00
let stmts_head = match block . stmts {
// Skip the last statement since there cannot possibly be any following statements that re-assign fields.
[ head @ .. , _ ] if ! head . is_empty ( ) = > head ,
_ = > return ,
} ;
for ( stmt_idx , stmt ) in stmts_head . iter ( ) . enumerate ( ) {
// find all binding statements like `let mut _ = T::default()` where `T::default()` is the
// `default` method of the `Default` trait, and store statement index in current block being
// checked and the name of the bound variable
2024-03-14 05:53:38 -05:00
let ( local , variant , binding_name , binding_type , span ) = if let StmtKind ::Let ( local ) = stmt . kind
2021-01-02 09:29:43 -06:00
// only take `let ...` statements
2023-11-16 12:13:24 -06:00
& & let Some ( expr ) = local . init
& & ! any_parent_is_automatically_derived ( cx . tcx , expr . hir_id )
& & ! expr . span . from_expansion ( )
2021-01-02 09:29:43 -06:00
// only take bindings to identifiers
2023-11-16 12:13:24 -06:00
& & let PatKind ::Binding ( _ , binding_id , ident , _ ) = local . pat . kind
2021-01-02 09:29:43 -06:00
// only when assigning `... = Default::default()`
2023-11-16 12:13:24 -06:00
& & is_expr_default ( expr , cx )
& & let binding_type = cx . typeck_results ( ) . node_type ( binding_id )
2024-02-07 11:17:52 -06:00
& & let ty ::Adt ( adt , args ) = * binding_type . kind ( )
2023-11-16 12:13:24 -06:00
& & adt . is_struct ( )
& & let variant = adt . non_enum_variant ( )
& & ( adt . did ( ) . is_local ( ) | | ! variant . is_field_list_non_exhaustive ( ) )
& & let module_did = cx . tcx . parent_module ( stmt . hir_id )
& & variant
2021-01-02 09:29:43 -06:00
. fields
. iter ( )
2023-11-16 12:13:24 -06:00
. all ( | field | field . vis . is_accessible_from ( module_did , cx . tcx ) )
& & let all_fields_are_copy = variant
2021-10-21 06:11:36 -05:00
. fields
. iter ( )
. all ( | field | {
2024-02-07 11:17:52 -06:00
is_copy ( cx , cx . tcx . type_of ( field . did ) . instantiate ( cx . tcx , args ) )
2023-11-16 12:13:24 -06:00
} )
& & ( ! has_drop ( cx , binding_type ) | | all_fields_are_copy )
{
( local , variant , ident . name , binding_type , expr . span )
} else {
continue ;
2021-01-02 09:29:43 -06:00
} ;
2020-11-05 07:29:48 -06:00
2023-03-24 08:04:35 -05:00
let init_ctxt = local . span . ctxt ( ) ;
2020-11-05 07:29:48 -06:00
// find all "later statement"'s where the fields of the binding set as
// Default::default() get reassigned, unless the reassignment refers to the original binding
let mut first_assign = None ;
let mut assigned_fields = Vec ::new ( ) ;
let mut cancel_lint = false ;
for consecutive_statement in & block . stmts [ stmt_idx + 1 .. ] {
// find out if and which field was set by this `consecutive_statement`
2021-01-02 09:29:43 -06:00
if let Some ( ( field_ident , assign_rhs ) ) = field_reassigned_by_stmt ( consecutive_statement , binding_name ) {
2020-11-05 07:29:48 -06:00
// interrupt and cancel lint if assign_rhs references the original binding
2023-03-24 08:04:35 -05:00
if contains_name ( binding_name , assign_rhs , cx ) | | init_ctxt ! = consecutive_statement . span . ctxt ( ) {
2020-11-05 07:29:48 -06:00
cancel_lint = true ;
break ;
}
// if the field was previously assigned, replace the assignment, otherwise insert the assignment
if let Some ( prev ) = assigned_fields
. iter_mut ( )
. find ( | ( field_name , _ ) | field_name = = & field_ident . name )
{
* prev = ( field_ident . name , assign_rhs ) ;
} else {
assigned_fields . push ( ( field_ident . name , assign_rhs ) ) ;
}
// also set first instance of error for help message
if first_assign . is_none ( ) {
first_assign = Some ( consecutive_statement ) ;
}
}
2021-01-02 09:29:43 -06:00
// interrupt if no field was assigned, since we only want to look at consecutive statements
2020-11-05 07:29:48 -06:00
else {
break ;
}
}
// if there are incorrectly assigned fields, do a span_lint_and_note to suggest
// construction using `Ty { fields, ..Default::default() }`
if ! assigned_fields . is_empty ( ) & & ! cancel_lint {
2021-01-02 09:29:43 -06:00
// if all fields of the struct are not assigned, add `.. Default::default()` to the suggestion.
let ext_with_default = ! variant
. fields
. iter ( )
2022-01-02 21:37:05 -06:00
. all ( | field | assigned_fields . iter ( ) . any ( | ( a , _ ) | a = = & field . name ) ) ;
2020-11-05 07:29:48 -06:00
2023-03-24 08:04:35 -05:00
let mut app = Applicability ::Unspecified ;
2021-01-02 09:29:43 -06:00
let field_list = assigned_fields
. into_iter ( )
. map ( | ( field , rhs ) | {
// extract and store the assigned value for help message
2023-03-24 08:04:35 -05:00
let value_snippet = snippet_with_context ( cx , rhs . span , init_ctxt , " .. " , & mut app ) . 0 ;
2022-10-06 02:44:38 -05:00
format! ( " {field} : {value_snippet} " )
2021-01-02 09:29:43 -06:00
} )
. collect ::< Vec < String > > ( )
. join ( " , " ) ;
2020-11-05 07:29:48 -06:00
2021-03-25 13:29:11 -05:00
// give correct suggestion if generics are involved (see #6944)
2023-11-16 12:13:24 -06:00
let binding_type = if let ty ::Adt ( adt_def , args ) = binding_type . kind ( )
& & ! args . is_empty ( )
{
let adt_def_ty_name = cx . tcx . item_name ( adt_def . did ( ) ) ;
let generic_args = args . iter ( ) . collect ::< Vec < _ > > ( ) ;
let tys_str = generic_args
. iter ( )
. map ( ToString ::to_string )
. collect ::< Vec < _ > > ( )
. join ( " , " ) ;
format! ( " {adt_def_ty_name} ::< {} > " , & tys_str )
} else {
binding_type . to_string ( )
2021-03-25 13:29:11 -05:00
} ;
2021-01-02 09:29:43 -06:00
let sugg = if ext_with_default {
if field_list . is_empty ( ) {
2022-10-06 02:44:38 -05:00
format! ( " {binding_type} ::default() " )
2020-11-05 07:29:48 -06:00
} else {
2022-10-06 02:44:38 -05:00
format! ( " {binding_type} {{ {field_list} , ..Default::default() }} " )
2021-01-02 09:29:43 -06:00
}
} else {
2022-10-06 02:44:38 -05:00
format! ( " {binding_type} {{ {field_list} }} " )
2021-01-02 09:29:43 -06:00
} ;
2020-11-05 07:29:48 -06:00
2021-01-02 09:29:43 -06:00
// span lint once per statement that binds default
span_lint_and_note (
cx ,
FIELD_REASSIGN_WITH_DEFAULT ,
first_assign . unwrap ( ) . span ,
" field assignment outside of initializer for an instance created with Default::default() " ,
Some ( local . span ) ,
2024-04-04 12:52:55 -05:00
format! ( " consider initializing the variable with ` {sugg} ` and removing relevant reassignments " ) ,
2021-01-02 09:29:43 -06:00
) ;
self . reassigned_linted . insert ( span ) ;
2020-11-05 07:29:48 -06:00
}
}
}
}
/// Checks if the given expression is the `default` method belonging to the `Default` trait.
fn is_expr_default < ' tcx > ( expr : & ' tcx Expr < ' tcx > , cx : & LateContext < ' tcx > ) -> bool {
2023-11-16 12:13:24 -06:00
if let ExprKind ::Call ( fn_expr , _ ) = & expr . kind
& & let ExprKind ::Path ( qpath ) = & fn_expr . kind
& & let Res ::Def ( _ , def_id ) = cx . qpath_res ( qpath , fn_expr . hir_id )
{
// right hand side of assignment is `Default::default`
cx . tcx . is_diagnostic_item ( sym ::default_fn , def_id )
} else {
false
2020-11-05 07:29:48 -06:00
}
}
/// Returns the reassigned field and the assigning expression (right-hand side of assign).
fn field_reassigned_by_stmt < ' tcx > ( this : & Stmt < ' tcx > , binding_name : Symbol ) -> Option < ( Ident , & ' tcx Expr < ' tcx > ) > {
2023-11-16 12:13:24 -06:00
if let StmtKind ::Semi ( later_expr ) = this . kind
2020-11-05 07:29:48 -06:00
// only take assignments
2023-11-16 12:13:24 -06:00
& & let ExprKind ::Assign ( assign_lhs , assign_rhs , _ ) = later_expr . kind
2020-11-05 07:29:48 -06:00
// only take assignments to fields where the left-hand side field is a field of
// the same binding as the previous statement
2023-11-16 12:13:24 -06:00
& & let ExprKind ::Field ( binding , field_ident ) = assign_lhs . kind
& & let ExprKind ::Path ( QPath ::Resolved ( _ , path ) ) = binding . kind
& & let Some ( second_binding_name ) = path . segments . last ( )
& & second_binding_name . ident . name = = binding_name
{
Some ( ( field_ident , assign_rhs ) )
} else {
None
2020-11-05 07:29:48 -06:00
}
}
2022-02-26 07:26:21 -06:00
/// 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 {
2023-11-16 12:13:24 -06:00
if let Some ( parent ) = get_parent_expr ( cx , expr )
& & let ExprKind ::Struct ( _ , _ , Some ( base ) ) = parent . kind
{
base . hir_id = = expr . hir_id
} else {
false
2022-02-26 07:26:21 -06:00
}
}