diff options
| author | yanglsh <yanglsh@shanghaitech.edu.cn> | 2025-06-01 01:29:44 +0800 |
|---|---|---|
| committer | yanglsh <yanglsh@shanghaitech.edu.cn> | 2025-06-01 13:29:46 +0800 |
| commit | 8029208130666a6f69dab5b1e47d3298cc50922f (patch) | |
| tree | f84426d1d1390a91f1e4be3752bf363b4dc8c881 | |
| parent | ebf39a54786ffb10a8ff5aec5504e4fbd29db19a (diff) | |
| download | rust-8029208130666a6f69dab5b1e47d3298cc50922f.tar.gz rust-8029208130666a6f69dab5b1e47d3298cc50922f.zip | |
Lint more cases in `unit_arg`
| -rw-r--r-- | clippy_lints/src/unit_types/unit_arg.rs | 70 | ||||
| -rw-r--r-- | tests/ui/unit_arg_fixable.fixed | 8 | ||||
| -rw-r--r-- | tests/ui/unit_arg_fixable.rs | 7 | ||||
| -rw-r--r-- | tests/ui/unit_arg_fixable.stderr | 14 |
4 files changed, 79 insertions, 20 deletions
diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs index 74b3331414c..ae6d8a1c1aa 100644 --- a/clippy_lints/src/unit_types/unit_arg.rs +++ b/clippy_lints/src/unit_types/unit_arg.rs @@ -1,6 +1,9 @@ +use std::iter; + use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; +use clippy_utils::ty::expr_type_is_certain; use clippy_utils::{is_expr_default, is_from_proc_macro}; use rustc_errors::Applicability; 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_ let arg_snippets_without_redundant_exprs: Vec<_> = args_to_recover .iter() .filter(|arg| !is_expr_default_nested(cx, arg) && (arg.span.from_expansion() || !is_empty_block(arg))) - .filter_map(|arg| get_expr_snippet(cx, arg)) + .filter_map(|arg| get_expr_snippet_with_type_certainty(cx, arg)) .collect(); if let Some(call_snippet) = expr.span.get_source_text(cx) { - let sugg = fmt_stmts_and_call( - cx, - expr, - &call_snippet, - &arg_snippets, - &arg_snippets_without_redundant_exprs, - ); - if arg_snippets_without_redundant_exprs.is_empty() && let suggestions = args_to_recover .iter() @@ -138,6 +133,13 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_ ); } else { let plural = arg_snippets_without_redundant_exprs.len() > 1; + let sugg = fmt_stmts_and_call( + cx, + expr, + &call_snippet, + arg_snippets, + arg_snippets_without_redundant_exprs, + ); let empty_or_s = if plural { "s" } else { "" }; let it_or_them = if plural { "them" } else { "it" }; db.span_suggestion( @@ -160,6 +162,20 @@ fn is_expr_default_nested<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) if block.expr.is_some() && is_expr_default_nested(cx, block.expr.unwrap())) } +enum MaybeTypeUncertain<'tcx> { + Certain(Sugg<'tcx>), + Uncertain(Sugg<'tcx>), +} + +impl From<MaybeTypeUncertain<'_>> for String { + fn from(value: MaybeTypeUncertain<'_>) -> Self { + match value { + MaybeTypeUncertain::Certain(sugg) => sugg.to_string(), + MaybeTypeUncertain::Uncertain(sugg) => format!("let _: () = {sugg}"), + } + } +} + fn get_expr_snippet<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<Sugg<'tcx>> { let mut app = Applicability::MachineApplicable; 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 Some(snip) } +fn get_expr_snippet_with_type_certainty<'tcx>( + cx: &LateContext<'tcx>, + expr: &'tcx Expr<'tcx>, +) -> Option<MaybeTypeUncertain<'tcx>> { + get_expr_snippet(cx, expr).map(|snip| { + // If the type of the expression is certain, we can use it directly. + // Otherwise, we wrap it in a `let _: () = ...` to ensure the type is correct. + if !expr_type_is_certain(cx, expr) && !is_block_with_no_expr(expr) { + MaybeTypeUncertain::Uncertain(snip) + } else { + MaybeTypeUncertain::Certain(snip) + } + }) +} + +fn is_block_with_no_expr(expr: &Expr<'_>) -> bool { + matches!(expr.kind, ExprKind::Block(Block { expr: None, .. }, _)) +} + fn is_empty_block(expr: &Expr<'_>) -> bool { matches!( expr.kind, @@ -188,23 +223,20 @@ fn fmt_stmts_and_call( cx: &LateContext<'_>, call_expr: &Expr<'_>, call_snippet: &str, - args_snippets: &[Sugg<'_>], - non_empty_block_args_snippets: &[Sugg<'_>], + args_snippets: Vec<Sugg<'_>>, + non_empty_block_args_snippets: Vec<MaybeTypeUncertain<'_>>, ) -> String { let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0); - let call_snippet_with_replacements = args_snippets.iter().fold(call_snippet.to_owned(), |acc, arg| { + let call_snippet_with_replacements = args_snippets.into_iter().fold(call_snippet.to_owned(), |acc, arg| { acc.replacen(&arg.to_string(), "()", 1) }); - let mut stmts_and_call = non_empty_block_args_snippets - .iter() - .map(ToString::to_string) - .collect::<Vec<_>>(); - stmts_and_call.push(call_snippet_with_replacements); - stmts_and_call = stmts_and_call + let stmts_and_call = non_empty_block_args_snippets .into_iter() + .map(Into::into) + .chain(iter::once(call_snippet_with_replacements)) .map(|v| reindent_multiline(&v, true, Some(call_expr_indent))) - .collect(); + .collect::<Vec<_>>(); let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent))); // expr is not in a block statement or result expression position, wrap in a block diff --git a/tests/ui/unit_arg_fixable.fixed b/tests/ui/unit_arg_fixable.fixed index 3ef6a953c72..03353a14081 100644 --- a/tests/ui/unit_arg_fixable.fixed +++ b/tests/ui/unit_arg_fixable.fixed @@ -67,4 +67,12 @@ fn issue14857() { mac!(empty_block); fn_take_unit(()); //~^ unit_arg + + fn def<T: Default>() -> T { + Default::default() + } + + let _: () = def(); + fn_take_unit(()); + //~^ unit_arg } diff --git a/tests/ui/unit_arg_fixable.rs b/tests/ui/unit_arg_fixable.rs index 097d51e9481..03fd96efdf9 100644 --- a/tests/ui/unit_arg_fixable.rs +++ b/tests/ui/unit_arg_fixable.rs @@ -61,4 +61,11 @@ fn issue14857() { //~^ unit_arg fn_take_unit(mac!(empty_block)); //~^ unit_arg + + fn def<T: Default>() -> T { + Default::default() + } + + fn_take_unit(def()); + //~^ unit_arg } diff --git a/tests/ui/unit_arg_fixable.stderr b/tests/ui/unit_arg_fixable.stderr index 94063f02a3f..ccd5aa8322f 100644 --- a/tests/ui/unit_arg_fixable.stderr +++ b/tests/ui/unit_arg_fixable.stderr @@ -94,5 +94,17 @@ LL ~ mac!(empty_block); LL ~ fn_take_unit(()); | -error: aborting due to 9 previous errors +error: passing a unit value to a function + --> tests/ui/unit_arg_fixable.rs:69:5 + | +LL | fn_take_unit(def()); + | ^^^^^^^^^^^^^^^^^^^ + | +help: move the expression in front of the call and replace it with the unit literal `()` + | +LL ~ let _: () = def(); +LL ~ fn_take_unit(()); + | + +error: aborting due to 10 previous errors |
