1
1
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
2
use clippy_utils:: macros:: macro_backtrace;
3
+ use clippy_utils:: source:: snippet_opt;
3
4
use clippy_utils:: ty:: expr_sig;
4
5
use clippy_utils:: { is_default_equivalent, path_def_id} ;
5
6
use rustc_errors:: Applicability ;
6
7
use rustc_hir:: def:: Res ;
7
8
use rustc_hir:: intravisit:: { walk_ty, Visitor } ;
8
- use rustc_hir:: { Block , Expr , ExprKind , Local , Node , QPath , TyKind } ;
9
+ use rustc_hir:: { Block , Expr , ExprKind , Local , Node , QPath , Ty , TyKind } ;
9
10
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
10
11
use rustc_middle:: lint:: in_external_macro;
11
12
use rustc_middle:: ty:: print:: with_forced_trimmed_paths;
@@ -41,13 +42,24 @@ declare_lint_pass!(BoxDefault => [BOX_DEFAULT]);
41
42
42
43
impl LateLintPass < ' _ > for BoxDefault {
43
44
fn check_expr ( & mut self , cx : & LateContext < ' _ > , expr : & Expr < ' _ > ) {
45
+ // If the expression is a call (`Box::new(...)`)
44
46
if let ExprKind :: Call ( box_new, [ arg] ) = expr. kind
47
+ // And call is of the form `<T>::something`
48
+ // Here, it would be `<Box>::new`
45
49
&& let ExprKind :: Path ( QPath :: TypeRelative ( ty, seg) ) = box_new. kind
46
- && let ExprKind :: Call ( arg_path, ..) = arg. kind
47
- && !in_external_macro ( cx. sess ( ) , expr. span )
48
- && ( expr. span . eq_ctxt ( arg. span ) || is_local_vec_expn ( cx, arg, expr) )
50
+ // And that method is `new`
49
51
&& seg. ident . name == sym:: new
52
+ // And the call is that of a `Box` method
50
53
&& path_def_id ( cx, ty) . map_or ( false , |id| Some ( id) == cx. tcx . lang_items ( ) . owned_box ( ) )
54
+ // And the single argument to the call is another function call
55
+ // This is the `T::default()` of `Box::new(T::default())`
56
+ && let ExprKind :: Call ( arg_path, inner_call_args) = arg. kind
57
+ // And we are not in a foreign crate's macro
58
+ && !in_external_macro ( cx. sess ( ) , expr. span )
59
+ // And the argument expression has the same context as the outer call expression
60
+ // or that we are inside a `vec!` macro expansion
61
+ && ( expr. span . eq_ctxt ( arg. span ) || is_local_vec_expn ( cx, arg, expr) )
62
+ // And the argument is equivalent to `Default::default()`
51
63
&& is_default_equivalent ( cx, arg)
52
64
{
53
65
span_lint_and_sugg (
@@ -59,7 +71,17 @@ impl LateLintPass<'_> for BoxDefault {
59
71
if is_plain_default ( cx, arg_path) || given_type ( cx, expr) {
60
72
"Box::default()" . into ( )
61
73
} else if let Some ( arg_ty) = cx. typeck_results ( ) . expr_ty ( arg) . make_suggestable ( cx. tcx , true ) {
62
- with_forced_trimmed_paths ! ( format!( "Box::<{arg_ty}>::default()" ) )
74
+ // Check if we can copy from the source expression in the replacement.
75
+ // We need the call to have no argument (see `explicit_default_type`).
76
+ if inner_call_args. is_empty ( )
77
+ && let Some ( ty) = explicit_default_type ( arg_path)
78
+ && let Some ( s) = snippet_opt ( cx, ty. span )
79
+ {
80
+ format ! ( "Box::<{s}>::default()" )
81
+ } else {
82
+ // Otherwise, use the inferred type's formatting.
83
+ with_forced_trimmed_paths ! ( format!( "Box::<{arg_ty}>::default()" ) )
84
+ }
63
85
} else {
64
86
return ;
65
87
} ,
@@ -81,6 +103,20 @@ fn is_plain_default(cx: &LateContext<'_>, arg_path: &Expr<'_>) -> bool {
81
103
}
82
104
}
83
105
106
+ // Checks whether the call is of the form `A::B::f()`. Returns `A::B` if it is.
107
+ //
108
+ // In the event we have this kind of construct, it's easy to use `A::B` as a replacement in the
109
+ // quickfix. `f` must however have no parameter. Should `f` have some, then some of the type of
110
+ // `A::B` may be inferred from the arguments. This would be the case for `Vec::from([0; false])`,
111
+ // where the argument to `from` allows inferring this is a `Vec<bool>`
112
+ fn explicit_default_type < ' a > ( arg_path : & ' a Expr < ' _ > ) -> Option < & ' a Ty < ' a > > {
113
+ if let ExprKind :: Path ( QPath :: TypeRelative ( ty, _) ) = & arg_path. kind {
114
+ Some ( ty)
115
+ } else {
116
+ None
117
+ }
118
+ }
119
+
84
120
fn is_local_vec_expn ( cx : & LateContext < ' _ > , expr : & Expr < ' _ > , ref_expr : & Expr < ' _ > ) -> bool {
85
121
macro_backtrace ( expr. span ) . next ( ) . map_or ( false , |call| {
86
122
cx. tcx . is_diagnostic_item ( sym:: vec_macro, call. def_id ) && call. span . eq_ctxt ( ref_expr. span )
0 commit comments