1
1
use clippy_utils:: diagnostics:: span_lint_and_then;
2
- use clippy_utils:: source:: { SourceText , SpanRangeExt , indent_of, reindent_multiline} ;
2
+ use clippy_utils:: source:: { SpanRangeExt , indent_of, reindent_multiline} ;
3
+ use clippy_utils:: sugg:: Sugg ;
3
4
use clippy_utils:: { is_expr_default, is_from_proc_macro} ;
4
5
use rustc_errors:: Applicability ;
5
6
use rustc_hir:: { Block , Expr , ExprKind , MatchSource , Node , StmtKind } ;
6
7
use rustc_lint:: LateContext ;
8
+ use rustc_span:: SyntaxContext ;
7
9
8
10
use super :: { UNIT_ARG , utils} ;
9
11
@@ -100,14 +102,16 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_
100
102
101
103
let arg_snippets: Vec < _ > = args_to_recover
102
104
. iter ( )
103
- . filter_map ( |arg| arg. span . get_source_text ( cx) )
105
+ // If the argument is from an expansion and is a `Default::default()`, we skip it
106
+ . filter ( |arg| !arg. span . from_expansion ( ) || !is_expr_default_nested ( cx, arg) )
107
+ . filter_map ( |arg| get_expr_snippet ( cx, arg) )
104
108
. collect ( ) ;
105
109
106
110
// If the argument is an empty block or `Default::default()`, we can replace it with `()`.
107
111
let arg_snippets_without_redundant_exprs: Vec < _ > = args_to_recover
108
112
. iter ( )
109
- . filter ( |arg| !is_empty_block ( arg) && ! is_expr_default ( cx , arg) )
110
- . filter_map ( |arg| arg . span . get_source_text ( cx) )
113
+ . filter ( |arg| !is_expr_default_nested ( cx , arg) && ( arg . span . from_expansion ( ) || ! is_empty_block ( arg) ) )
114
+ . filter_map ( |arg| get_expr_snippet ( cx, arg ) )
111
115
. collect ( ) ;
112
116
113
117
if let Some ( call_snippet) = expr. span . get_source_text ( cx) {
@@ -119,13 +123,17 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_
119
123
& arg_snippets_without_redundant_exprs,
120
124
) ;
121
125
122
- if arg_snippets_without_redundant_exprs. is_empty ( ) {
126
+ if arg_snippets_without_redundant_exprs. is_empty ( )
127
+ && let suggestions = args_to_recover
128
+ . iter ( )
129
+ . filter ( |arg| !arg. span . from_expansion ( ) || !is_expr_default_nested ( cx, arg) )
130
+ . map ( |arg| ( arg. span . parent_callsite ( ) . unwrap_or ( arg. span ) , "()" . to_string ( ) ) )
131
+ . collect :: < Vec < _ > > ( )
132
+ && !suggestions. is_empty ( )
133
+ {
123
134
db. multipart_suggestion (
124
135
format ! ( "use {singular}unit literal{plural} instead" ) ,
125
- args_to_recover
126
- . iter ( )
127
- . map ( |arg| ( arg. span , "()" . to_string ( ) ) )
128
- . collect :: < Vec < _ > > ( ) ,
136
+ suggestions,
129
137
applicability,
130
138
) ;
131
139
} else {
@@ -146,6 +154,22 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_
146
154
) ;
147
155
}
148
156
157
+ fn is_expr_default_nested < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> bool {
158
+ is_expr_default ( cx, expr)
159
+ || matches ! ( expr. kind, ExprKind :: Block ( block, _)
160
+ if block. expr. is_some( ) && is_expr_default_nested( cx, block. expr. unwrap( ) ) )
161
+ }
162
+
163
+ fn get_expr_snippet < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> Option < Sugg < ' tcx > > {
164
+ let mut app = Applicability :: MachineApplicable ;
165
+ let snip = Sugg :: hir_with_context ( cx, expr, SyntaxContext :: root ( ) , ".." , & mut app) ;
166
+ if app != Applicability :: MachineApplicable {
167
+ return None ;
168
+ }
169
+
170
+ Some ( snip)
171
+ }
172
+
149
173
fn is_empty_block ( expr : & Expr < ' _ > ) -> bool {
150
174
matches ! (
151
175
expr. kind,
@@ -164,17 +188,17 @@ fn fmt_stmts_and_call(
164
188
cx : & LateContext < ' _ > ,
165
189
call_expr : & Expr < ' _ > ,
166
190
call_snippet : & str ,
167
- args_snippets : & [ SourceText ] ,
168
- non_empty_block_args_snippets : & [ SourceText ] ,
191
+ args_snippets : & [ Sugg < ' _ > ] ,
192
+ non_empty_block_args_snippets : & [ Sugg < ' _ > ] ,
169
193
) -> String {
170
194
let call_expr_indent = indent_of ( cx, call_expr. span ) . unwrap_or ( 0 ) ;
171
- let call_snippet_with_replacements = args_snippets
172
- . iter ( )
173
- . fold ( call_snippet . to_owned ( ) , |acc , arg| acc . replacen ( arg . as_ref ( ) , "()" , 1 ) ) ;
195
+ let call_snippet_with_replacements = args_snippets. iter ( ) . fold ( call_snippet . to_owned ( ) , |acc , arg| {
196
+ acc . replacen ( & arg . to_string ( ) , "()" , 1 )
197
+ } ) ;
174
198
175
199
let mut stmts_and_call = non_empty_block_args_snippets
176
200
. iter ( )
177
- . map ( |it| it. as_ref ( ) . to_owned ( ) )
201
+ . map ( |it| it. to_string ( ) )
178
202
. collect :: < Vec < _ > > ( ) ;
179
203
stmts_and_call. push ( call_snippet_with_replacements) ;
180
204
stmts_and_call = stmts_and_call
0 commit comments