11use clippy_utils:: diagnostics:: span_lint_and_then;
22use clippy_utils:: source:: { SpanRangeExt , indent_of, reindent_multiline} ;
33use clippy_utils:: sugg:: Sugg ;
4+ use clippy_utils:: ty:: expr_type_is_certain;
45use clippy_utils:: { is_expr_default, is_from_proc_macro} ;
56use rustc_errors:: Applicability ;
67use rustc_hir:: { Block , Expr , ExprKind , MatchSource , Node , StmtKind } ;
@@ -111,7 +112,7 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_
111112 let arg_snippets_without_redundant_exprs: Vec < _ > = args_to_recover
112113 . iter ( )
113114 . 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) )
115+ . filter_map ( |arg| get_expr_snippet_with_type_certainty ( cx, arg) )
115116 . collect ( ) ;
116117
117118 if let Some ( call_snippet) = expr. span . get_source_text ( cx) {
@@ -160,6 +161,20 @@ fn is_expr_default_nested<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>)
160161 if block. expr. is_some( ) && is_expr_default_nested( cx, block. expr. unwrap( ) ) )
161162}
162163
164+ enum MaybeTypeUncertain < ' tcx > {
165+ Certain ( Sugg < ' tcx > ) ,
166+ Uncertain ( Sugg < ' tcx > ) ,
167+ }
168+
169+ impl ToString for MaybeTypeUncertain < ' _ > {
170+ fn to_string ( & self ) -> String {
171+ match self {
172+ MaybeTypeUncertain :: Certain ( sugg) => sugg. to_string ( ) ,
173+ MaybeTypeUncertain :: Uncertain ( sugg) => format ! ( "let _: () = {}" , sugg) ,
174+ }
175+ }
176+ }
177+
163178fn get_expr_snippet < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> Option < Sugg < ' tcx > > {
164179 let mut app = Applicability :: MachineApplicable ;
165180 let snip = Sugg :: hir_with_context ( cx, expr, SyntaxContext :: root ( ) , ".." , & mut app) ;
@@ -170,6 +185,25 @@ fn get_expr_snippet<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opt
170185 Some ( snip)
171186}
172187
188+ fn get_expr_snippet_with_type_certainty < ' tcx > (
189+ cx : & LateContext < ' tcx > ,
190+ expr : & ' tcx Expr < ' tcx > ,
191+ ) -> Option < MaybeTypeUncertain < ' tcx > > {
192+ get_expr_snippet ( cx, expr) . map ( |snip| {
193+ // If the type of the expression is certain, we can use it directly.
194+ // Otherwise, we wrap it in a `let _: () = ...` to ensure the type is correct.
195+ if !expr_type_is_certain ( cx, expr) && !is_block_with_no_expr ( expr) {
196+ MaybeTypeUncertain :: Uncertain ( snip)
197+ } else {
198+ MaybeTypeUncertain :: Certain ( snip)
199+ }
200+ } )
201+ }
202+
203+ fn is_block_with_no_expr ( expr : & Expr < ' _ > ) -> bool {
204+ matches ! ( expr. kind, ExprKind :: Block ( Block { expr: None , .. } , _) )
205+ }
206+
173207fn is_empty_block ( expr : & Expr < ' _ > ) -> bool {
174208 matches ! (
175209 expr. kind,
@@ -189,7 +223,7 @@ fn fmt_stmts_and_call(
189223 call_expr : & Expr < ' _ > ,
190224 call_snippet : & str ,
191225 args_snippets : & [ Sugg < ' _ > ] ,
192- non_empty_block_args_snippets : & [ Sugg < ' _ > ] ,
226+ non_empty_block_args_snippets : & [ MaybeTypeUncertain < ' _ > ] ,
193227) -> String {
194228 let call_expr_indent = indent_of ( cx, call_expr. span ) . unwrap_or ( 0 ) ;
195229 let call_snippet_with_replacements = args_snippets. iter ( ) . fold ( call_snippet. to_owned ( ) , |acc, arg| {
0 commit comments