1+ use std:: iter;
2+
13use clippy_utils:: diagnostics:: span_lint_and_then;
24use clippy_utils:: source:: { SpanRangeExt , indent_of, reindent_multiline} ;
35use clippy_utils:: sugg:: Sugg ;
6+ use clippy_utils:: ty:: expr_type_is_certain;
47use clippy_utils:: { is_expr_default, is_from_proc_macro} ;
58use rustc_errors:: Applicability ;
69use 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_
111114 let arg_snippets_without_redundant_exprs: Vec < _ > = args_to_recover
112115 . iter ( )
113116 . 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) )
115118 . collect ( ) ;
116119
117120 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-
126121 if arg_snippets_without_redundant_exprs. is_empty ( )
127122 && let suggestions = args_to_recover
128123 . iter ( )
@@ -138,6 +133,13 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_
138133 ) ;
139134 } else {
140135 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+ ) ;
141143 let empty_or_s = if plural { "s" } else { "" } ;
142144 let it_or_them = if plural { "them" } else { "it" } ;
143145 db. span_suggestion (
@@ -160,6 +162,20 @@ fn is_expr_default_nested<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>)
160162 if block. expr. is_some( ) && is_expr_default_nested( cx, block. expr. unwrap( ) ) )
161163}
162164
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+
163179fn get_expr_snippet < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> Option < Sugg < ' tcx > > {
164180 let mut app = Applicability :: MachineApplicable ;
165181 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
170186 Some ( snip)
171187}
172188
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+
173208fn is_empty_block ( expr : & Expr < ' _ > ) -> bool {
174209 matches ! (
175210 expr. kind,
@@ -188,23 +223,20 @@ fn fmt_stmts_and_call(
188223 cx : & LateContext < ' _ > ,
189224 call_expr : & Expr < ' _ > ,
190225 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 < ' _ > > ,
193228) -> String {
194229 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| {
196231 acc. replacen ( & arg. to_string ( ) , "()" , 1 )
197232 } ) ;
198233
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
205235 . into_iter ( )
236+ . map ( Into :: into)
237+ . chain ( iter:: once ( call_snippet_with_replacements) )
206238 . map ( |v| reindent_multiline ( & v, true , Some ( call_expr_indent) ) )
207- . collect ( ) ;
239+ . collect :: < Vec < _ > > ( ) ;
208240
209241 let mut stmts_and_call_snippet = stmts_and_call. join ( & format ! ( "{}{}" , ";\n " , " " . repeat( call_expr_indent) ) ) ;
210242 // expr is not in a block statement or result expression position, wrap in a block
0 commit comments