Skip to content

Commit 8029208

Browse files
committed
Lint more cases in unit_arg
1 parent ebf39a5 commit 8029208

File tree

4 files changed

+79
-20
lines changed

4 files changed

+79
-20
lines changed

clippy_lints/src/unit_types/unit_arg.rs

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
use std::iter;
2+
13
use clippy_utils::diagnostics::span_lint_and_then;
24
use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline};
35
use clippy_utils::sugg::Sugg;
6+
use clippy_utils::ty::expr_type_is_certain;
47
use clippy_utils::{is_expr_default, is_from_proc_macro};
58
use rustc_errors::Applicability;
69
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_
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+
163179
fn 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+
173208
fn 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

tests/ui/unit_arg_fixable.fixed

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,12 @@ fn issue14857() {
6767
mac!(empty_block);
6868
fn_take_unit(());
6969
//~^ unit_arg
70+
71+
fn def<T: Default>() -> T {
72+
Default::default()
73+
}
74+
75+
let _: () = def();
76+
fn_take_unit(());
77+
//~^ unit_arg
7078
}

tests/ui/unit_arg_fixable.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,11 @@ fn issue14857() {
6161
//~^ unit_arg
6262
fn_take_unit(mac!(empty_block));
6363
//~^ unit_arg
64+
65+
fn def<T: Default>() -> T {
66+
Default::default()
67+
}
68+
69+
fn_take_unit(def());
70+
//~^ unit_arg
6471
}

tests/ui/unit_arg_fixable.stderr

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,5 +94,17 @@ LL ~ mac!(empty_block);
9494
LL ~ fn_take_unit(());
9595
|
9696

97-
error: aborting due to 9 previous errors
97+
error: passing a unit value to a function
98+
--> tests/ui/unit_arg_fixable.rs:69:5
99+
|
100+
LL | fn_take_unit(def());
101+
| ^^^^^^^^^^^^^^^^^^^
102+
|
103+
help: move the expression in front of the call and replace it with the unit literal `()`
104+
|
105+
LL ~ let _: () = def();
106+
LL ~ fn_take_unit(());
107+
|
108+
109+
error: aborting due to 10 previous errors
98110

0 commit comments

Comments
 (0)