1
+ use std:: iter;
2
+
1
3
use clippy_utils:: diagnostics:: span_lint_and_then;
2
4
use clippy_utils:: source:: { SpanRangeExt , indent_of, reindent_multiline} ;
3
5
use clippy_utils:: sugg:: Sugg ;
6
+ use clippy_utils:: ty:: expr_type_is_certain;
4
7
use clippy_utils:: { is_expr_default, is_from_proc_macro} ;
5
8
use rustc_errors:: Applicability ;
6
9
use rustc_hir:: { Block , Expr , ExprKind , MatchSource , Node , StmtKind } ;
@@ -111,18 +114,10 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_
111
114
let arg_snippets_without_redundant_exprs: Vec < _ > = args_to_recover
112
115
. iter ( )
113
116
. 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) )
117
+ . filter_map ( |arg| get_expr_snippet_with_type_certainty ( cx, arg) )
115
118
. collect ( ) ;
116
119
117
120
if let Some ( call_snippet) = expr. span . get_source_text ( cx) {
118
- let sugg = fmt_stmts_and_call (
119
- cx,
120
- expr,
121
- & call_snippet,
122
- & arg_snippets,
123
- & arg_snippets_without_redundant_exprs,
124
- ) ;
125
-
126
121
if arg_snippets_without_redundant_exprs. is_empty ( )
127
122
&& let suggestions = args_to_recover
128
123
. iter ( )
@@ -138,6 +133,13 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_
138
133
) ;
139
134
} else {
140
135
let plural = arg_snippets_without_redundant_exprs. len ( ) > 1 ;
136
+ let sugg = fmt_stmts_and_call (
137
+ cx,
138
+ expr,
139
+ & call_snippet,
140
+ arg_snippets,
141
+ arg_snippets_without_redundant_exprs,
142
+ ) ;
141
143
let empty_or_s = if plural { "s" } else { "" } ;
142
144
let it_or_them = if plural { "them" } else { "it" } ;
143
145
db. span_suggestion (
@@ -160,6 +162,20 @@ fn is_expr_default_nested<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>)
160
162
if block. expr. is_some( ) && is_expr_default_nested( cx, block. expr. unwrap( ) ) )
161
163
}
162
164
165
+ enum MaybeTypeUncertain < ' tcx > {
166
+ Certain ( Sugg < ' tcx > ) ,
167
+ Uncertain ( Sugg < ' tcx > ) ,
168
+ }
169
+
170
+ impl From < MaybeTypeUncertain < ' _ > > for String {
171
+ fn from ( value : MaybeTypeUncertain < ' _ > ) -> Self {
172
+ match value {
173
+ MaybeTypeUncertain :: Certain ( sugg) => sugg. to_string ( ) ,
174
+ MaybeTypeUncertain :: Uncertain ( sugg) => format ! ( "let _: () = {sugg}" ) ,
175
+ }
176
+ }
177
+ }
178
+
163
179
fn get_expr_snippet < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> Option < Sugg < ' tcx > > {
164
180
let mut app = Applicability :: MachineApplicable ;
165
181
let snip = Sugg :: hir_with_context ( cx, expr, SyntaxContext :: root ( ) , ".." , & mut app) ;
@@ -170,6 +186,25 @@ fn get_expr_snippet<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opt
170
186
Some ( snip)
171
187
}
172
188
189
+ fn get_expr_snippet_with_type_certainty < ' tcx > (
190
+ cx : & LateContext < ' tcx > ,
191
+ expr : & ' tcx Expr < ' tcx > ,
192
+ ) -> Option < MaybeTypeUncertain < ' tcx > > {
193
+ get_expr_snippet ( cx, expr) . map ( |snip| {
194
+ // If the type of the expression is certain, we can use it directly.
195
+ // Otherwise, we wrap it in a `let _: () = ...` to ensure the type is correct.
196
+ if !expr_type_is_certain ( cx, expr) && !is_block_with_no_expr ( expr) {
197
+ MaybeTypeUncertain :: Uncertain ( snip)
198
+ } else {
199
+ MaybeTypeUncertain :: Certain ( snip)
200
+ }
201
+ } )
202
+ }
203
+
204
+ fn is_block_with_no_expr ( expr : & Expr < ' _ > ) -> bool {
205
+ matches ! ( expr. kind, ExprKind :: Block ( Block { expr: None , .. } , _) )
206
+ }
207
+
173
208
fn is_empty_block ( expr : & Expr < ' _ > ) -> bool {
174
209
matches ! (
175
210
expr. kind,
@@ -188,23 +223,20 @@ fn fmt_stmts_and_call(
188
223
cx : & LateContext < ' _ > ,
189
224
call_expr : & Expr < ' _ > ,
190
225
call_snippet : & str ,
191
- args_snippets : & [ Sugg < ' _ > ] ,
192
- non_empty_block_args_snippets : & [ Sugg < ' _ > ] ,
226
+ args_snippets : Vec < Sugg < ' _ > > ,
227
+ non_empty_block_args_snippets : Vec < MaybeTypeUncertain < ' _ > > ,
193
228
) -> String {
194
229
let call_expr_indent = indent_of ( cx, call_expr. span ) . unwrap_or ( 0 ) ;
195
- let call_snippet_with_replacements = args_snippets. iter ( ) . fold ( call_snippet. to_owned ( ) , |acc, arg| {
230
+ let call_snippet_with_replacements = args_snippets. into_iter ( ) . fold ( call_snippet. to_owned ( ) , |acc, arg| {
196
231
acc. replacen ( & arg. to_string ( ) , "()" , 1 )
197
232
} ) ;
198
233
199
- let mut stmts_and_call = non_empty_block_args_snippets
200
- . iter ( )
201
- . map ( ToString :: to_string)
202
- . collect :: < Vec < _ > > ( ) ;
203
- stmts_and_call. push ( call_snippet_with_replacements) ;
204
- stmts_and_call = stmts_and_call
234
+ let stmts_and_call = non_empty_block_args_snippets
205
235
. into_iter ( )
236
+ . map ( Into :: into)
237
+ . chain ( iter:: once ( call_snippet_with_replacements) )
206
238
. map ( |v| reindent_multiline ( & v, true , Some ( call_expr_indent) ) )
207
- . collect ( ) ;
239
+ . collect :: < Vec < _ > > ( ) ;
208
240
209
241
let mut stmts_and_call_snippet = stmts_and_call. join ( & format ! ( "{}{}" , ";\n " , " " . repeat( call_expr_indent) ) ) ;
210
242
// expr is not in a block statement or result expression position, wrap in a block
0 commit comments