11use 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 ;
34use clippy_utils:: { is_expr_default, is_from_proc_macro} ;
45use rustc_errors:: Applicability ;
56use rustc_hir:: { Block , Expr , ExprKind , MatchSource , Node , StmtKind } ;
67use rustc_lint:: LateContext ;
8+ use rustc_span:: SyntaxContext ;
79
810use super :: { UNIT_ARG , utils} ;
911
@@ -100,14 +102,16 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_
100102
101103 let arg_snippets: Vec < _ > = args_to_recover
102104 . 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) )
104108 . collect ( ) ;
105109
106110 // If the argument is an empty block or `Default::default()`, we can replace it with `()`.
107111 let arg_snippets_without_redundant_exprs: Vec < _ > = args_to_recover
108112 . 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 ) )
111115 . collect ( ) ;
112116
113117 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_
119123 & arg_snippets_without_redundant_exprs,
120124 ) ;
121125
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+ {
123134 db. multipart_suggestion (
124135 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,
129137 applicability,
130138 ) ;
131139 } else {
@@ -146,6 +154,22 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_
146154 ) ;
147155}
148156
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+
149173fn is_empty_block ( expr : & Expr < ' _ > ) -> bool {
150174 matches ! (
151175 expr. kind,
@@ -164,17 +188,17 @@ fn fmt_stmts_and_call(
164188 cx : & LateContext < ' _ > ,
165189 call_expr : & Expr < ' _ > ,
166190 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 < ' _ > ] ,
169193) -> String {
170194 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+ } ) ;
174198
175199 let mut stmts_and_call = non_empty_block_args_snippets
176200 . iter ( )
177- . map ( |it| it . as_ref ( ) . to_owned ( ) )
201+ . map ( ToString :: to_string )
178202 . collect :: < Vec < _ > > ( ) ;
179203 stmts_and_call. push ( call_snippet_with_replacements) ;
180204 stmts_and_call = stmts_and_call
0 commit comments