Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve the suggestion of the lint unit-arg #5931

Merged
merged 4 commits into from
Sep 10, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 31 additions & 10 deletions clippy_lints/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ use rustc_typeck::hir_ty_to_ty;
use crate::consts::{constant, Constant};
use crate::utils::paths;
use crate::utils::{
clip, comparisons, differing_macro_contexts, higher, in_constant, int_bits, is_type_diagnostic_item,
clip, comparisons, differing_macro_contexts, higher, in_constant, indent_of, int_bits, is_type_diagnostic_item,
last_path_segment, match_def_path, match_path, method_chain_args, multispan_sugg, numeric_literal::NumericLiteral,
qpath_res, sext, snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite, span_lint,
span_lint_and_help, span_lint_and_sugg, span_lint_and_then, unsext,
span_lint_and_help, span_lint_and_sugg, span_lint_and_then, trim_multiline, unsext,
};

declare_clippy_lint! {
Expand Down Expand Up @@ -802,6 +802,7 @@ impl<'tcx> LateLintPass<'tcx> for UnitArg {
}
}

#[allow(clippy::too_many_lines)]
fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) {
let mut applicability = Applicability::MachineApplicable;
let (singular, plural) = if args_to_recover.len() > 1 {
Expand Down Expand Up @@ -856,18 +857,38 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp
.filter(|arg| !is_empty_block(arg))
.filter_map(|arg| snippet_opt(cx, arg.span))
.collect();
let indent = indent_of(cx, expr.span).unwrap_or(0);

if let Some(mut sugg) = snippet_opt(cx, expr.span) {
arg_snippets.iter().for_each(|arg| {
sugg = sugg.replacen(arg, "()", 1);
});
sugg = format!("{}{}{}", arg_snippets_without_empty_blocks.join("; "), "; ", sugg);
if let Some(expr_str) = snippet_opt(cx, expr.span) {
let expr_with_replacements = arg_snippets
.iter()
.fold(expr_str, |acc, arg| acc.replacen(arg, "()", 1));

// expr is not in a block statement or result expression position, wrap in a block
let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(expr.hir_id));
if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) {
// expr is not in a block statement or result expression position, wrap in a block
sugg = format!("{{ {} }}", sugg);
let wrap_in_block =
!matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_)));

let stmts_indent = if wrap_in_block { indent + 4 } else { indent };
let mut stmts_and_call = arg_snippets_without_empty_blocks.clone();
stmts_and_call.push(expr_with_replacements);
let mut stmts_and_call_str = stmts_and_call
.into_iter()
.enumerate()
.map(|(i, v)| {
let with_indent_prefix = if i > 0 { " ".repeat(stmts_indent) + &v } else { v };
trim_multiline(with_indent_prefix.into(), true, Some(stmts_indent)).into_owned()
})
.collect::<Vec<String>>()
.join(";\n");

if wrap_in_block {
stmts_and_call_str = " ".repeat(stmts_indent) + &stmts_and_call_str;
stmts_and_call_str = format!("{{\n{}\n{}}}", &stmts_and_call_str, " ".repeat(indent));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to wrap this wrap_in_block handling code into its own function to get rid of the allow above?

Copy link
Contributor Author

@tnielens tnielens Sep 2, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved that logic out. That clarifies the code a bit 👍.
I had to modify and rename the trim_multiline utils method to let it indent code further as it only supported dedentation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in case you missed the notif @flip1995 (sorry if have no time atm)

}

let sugg = stmts_and_call_str;

if arg_snippets_without_empty_blocks.is_empty() {
db.multipart_suggestion(
&format!("use {}unit literal{} instead", singular, plural),
Expand Down
2 changes: 1 addition & 1 deletion clippy_lints/src/utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -662,7 +662,7 @@ pub fn expr_block<'a, T: LintContext>(

/// Trim indentation from a multiline string with possibility of ignoring the
/// first line.
fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
pub fn trim_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
let s_space = trim_multiline_inner(s, ignore_first, indent, ' ');
let s_tab = trim_multiline_inner(s_space, ignore_first, indent, '\t');
trim_multiline_inner(s_tab, ignore_first, indent, ' ')
Expand Down
8 changes: 7 additions & 1 deletion tests/ui/unit_arg.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
#![warn(clippy::unit_arg)]
#![allow(clippy::no_effect, unused_must_use, unused_variables, clippy::unused_unit)]
#![allow(
clippy::no_effect,
unused_must_use,
unused_variables,
clippy::unused_unit,
clippy::or_fun_call
)]

use std::fmt::Debug;

Expand Down
79 changes: 42 additions & 37 deletions tests/ui/unit_arg.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: passing a unit value to a function
--> $DIR/unit_arg.rs:23:5
--> $DIR/unit_arg.rs:29:5
|
LL | / foo({
LL | | 1;
Expand All @@ -15,22 +15,24 @@ help: or move the expression in front of the call and replace it with the unit l
|
LL | {
LL | 1;
LL | }; foo(());
LL | };
LL | foo(());
|

error: passing a unit value to a function
--> $DIR/unit_arg.rs:26:5
--> $DIR/unit_arg.rs:32:5
|
LL | foo(foo(1));
| ^^^^^^^^^^^
|
help: move the expression in front of the call and replace it with the unit literal `()`
|
LL | foo(1); foo(());
| ^^^^^^^^^^^^^^^
LL | foo(1);
LL | foo(());
|

error: passing a unit value to a function
--> $DIR/unit_arg.rs:27:5
--> $DIR/unit_arg.rs:33:5
|
LL | / foo({
LL | | foo(1);
Expand All @@ -47,11 +49,12 @@ help: or move the expression in front of the call and replace it with the unit l
LL | {
LL | foo(1);
LL | foo(2);
LL | }; foo(());
LL | };
LL | foo(());
|

error: passing a unit value to a function
--> $DIR/unit_arg.rs:32:5
--> $DIR/unit_arg.rs:38:5
|
LL | / b.bar({
LL | | 1;
Expand All @@ -66,22 +69,25 @@ help: or move the expression in front of the call and replace it with the unit l
|
LL | {
LL | 1;
LL | }; b.bar(());
LL | };
LL | b.bar(());
|

error: passing unit values to a function
--> $DIR/unit_arg.rs:35:5
--> $DIR/unit_arg.rs:41:5
|
LL | taking_multiple_units(foo(0), foo(1));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: move the expressions in front of the call and replace them with the unit literal `()`
|
LL | foo(0); foo(1); taking_multiple_units((), ());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | foo(0);
LL | foo(1);
LL | taking_multiple_units((), ());
|

error: passing unit values to a function
--> $DIR/unit_arg.rs:36:5
--> $DIR/unit_arg.rs:42:5
|
LL | / taking_multiple_units(foo(0), {
LL | | foo(1);
Expand All @@ -95,14 +101,16 @@ LL | foo(2)
|
help: or move the expressions in front of the call and replace them with the unit literal `()`
|
LL | foo(0); {
LL | foo(0);
LL | {
LL | foo(1);
LL | foo(2);
LL | }; taking_multiple_units((), ());
LL | };
LL | taking_multiple_units((), ());
|

error: passing unit values to a function
--> $DIR/unit_arg.rs:40:5
--> $DIR/unit_arg.rs:46:5
|
LL | / taking_multiple_units(
LL | | {
Expand All @@ -124,53 +132,50 @@ LL | foo(3)
help: or move the expressions in front of the call and replace them with the unit literal `()`
|
LL | {
LL | foo(0);
LL | foo(1);
LL | }; {
LL | foo(2);
LL | foo(3);
LL | foo(0);
LL | foo(1);
LL | };
LL | {
LL | foo(2);
...

error: use of `or` followed by a function call
--> $DIR/unit_arg.rs:51:10
|
LL | None.or(Some(foo(2)));
| ^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(foo(2)))`
|
= note: `-D clippy::or-fun-call` implied by `-D warnings`

error: passing a unit value to a function
--> $DIR/unit_arg.rs:51:13
--> $DIR/unit_arg.rs:57:13
|
LL | None.or(Some(foo(2)));
| ^^^^^^^^^^^^
|
help: move the expression in front of the call and replace it with the unit literal `()`
|
LL | None.or({ foo(2); Some(()) });
| ^^^^^^^^^^^^^^^^^^^^
LL | None.or({
LL | foo(2);
LL | Some(())
LL | });
|

error: passing a unit value to a function
--> $DIR/unit_arg.rs:54:5
--> $DIR/unit_arg.rs:60:5
|
LL | foo(foo(()))
| ^^^^^^^^^^^^
|
help: move the expression in front of the call and replace it with the unit literal `()`
|
LL | foo(()); foo(())
LL | foo(());
LL | foo(())
|

error: passing a unit value to a function
--> $DIR/unit_arg.rs:87:5
--> $DIR/unit_arg.rs:93:5
|
LL | Some(foo(1))
| ^^^^^^^^^^^^
|
help: move the expression in front of the call and replace it with the unit literal `()`
|
LL | foo(1); Some(())
LL | foo(1);
LL | Some(())
|

error: aborting due to 11 previous errors
error: aborting due to 10 previous errors

11 changes: 7 additions & 4 deletions tests/ui/unit_arg_empty_blocks.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ LL | taking_two_units({}, foo(0));
|
help: move the expression in front of the call and replace it with the unit literal `()`
|
LL | foo(0); taking_two_units((), ());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | foo(0);
LL | taking_two_units((), ());
|

error: passing unit values to a function
--> $DIR/unit_arg_empty_blocks.rs:18:5
Expand All @@ -35,8 +36,10 @@ LL | taking_three_units({}, foo(0), foo(1));
|
help: move the expressions in front of the call and replace them with the unit literal `()`
|
LL | foo(0); foo(1); taking_three_units((), (), ());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | foo(0);
LL | foo(1);
LL | taking_three_units((), (), ());
|

error: aborting due to 4 previous errors