fix: map_with_unused_argument_over_ranges wrongly unmangled macros

This commit is contained in:
Linshu Yang
2026-01-05 18:35:45 +00:00
committed by linshuy2
parent d3ca65d04a
commit a255ac1d82
4 changed files with 79 additions and 35 deletions
@@ -1,7 +1,7 @@
use crate::methods::MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::Sugg;
use clippy_utils::{eager_or_lazy, higher, std_or_core, usage};
use rustc_ast::LitKind;
@@ -10,12 +10,13 @@
use rustc_errors::Applicability;
use rustc_hir::{Body, Closure, Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_span::Span;
use rustc_span::{Span, SyntaxContext};
fn extract_count_with_applicability(
cx: &LateContext<'_>,
range: higher::Range<'_>,
applicability: &mut Applicability,
ctxt: SyntaxContext,
) -> Option<String> {
let start = range.start?;
let end = range.end?;
@@ -40,7 +41,7 @@ fn extract_count_with_applicability(
};
return Some(format!("{count}"));
}
let end_snippet = Sugg::hir_with_applicability(cx, end, "...", applicability)
let end_snippet = Sugg::hir_with_context(cx, end, ctxt, "...", applicability)
.maybe_paren()
.into_string();
if lower_bound == 0 {
@@ -74,45 +75,23 @@ pub(super) fn check(
value: body_expr,
} = body_hir
&& !usage::BindingUsageFinder::are_params_used(cx, body_hir)
&& let Some(count) = extract_count_with_applicability(cx, range, &mut applicability)
&& let ctxt = ex.span.ctxt()
&& let Some(count) = extract_count_with_applicability(cx, range, &mut applicability, ctxt)
&& let Some(exec_context) = std_or_core(cx)
{
let method_to_use_name;
let new_span;
let use_take;
if eager_or_lazy::switch_to_eager_eval(cx, body_expr) {
let (method_to_use_name, new_span, use_take) = if eager_or_lazy::switch_to_eager_eval(cx, body_expr) {
if msrv.meets(cx, msrvs::REPEAT_N) {
method_to_use_name = "repeat_n";
let body_snippet = snippet_with_applicability(cx, body_expr.span, "..", &mut applicability);
new_span = (arg.span, format!("{body_snippet}, {count}"));
use_take = false;
let (body_snippet, _) = snippet_with_context(cx, body_expr.span, ctxt, "..", &mut applicability);
("repeat_n", (arg.span, format!("{body_snippet}, {count}")), false)
} else {
method_to_use_name = "repeat";
let body_snippet = snippet_with_applicability(cx, body_expr.span, "..", &mut applicability);
new_span = (arg.span, body_snippet.to_string());
use_take = true;
let (body_snippet, _) = snippet_with_context(cx, body_expr.span, ctxt, "..", &mut applicability);
("repeat", (arg.span, body_snippet.to_string()), true)
}
} else if msrv.meets(cx, msrvs::REPEAT_WITH) {
method_to_use_name = "repeat_with";
new_span = (param.span, String::new());
use_take = true;
("repeat_with", (param.span, String::new()), true)
} else {
return;
}
// We need to provide nonempty parts to diag.multipart_suggestion so we
// collate all our parts here and then remove those that are empty.
let mut parts = vec![
(
ex.span.with_hi(method_name_span.hi()),
format!("{exec_context}::iter::{method_to_use_name}"),
),
new_span,
];
if use_take {
parts.push((ex.span.shrink_to_hi(), format!(".take({count})")));
}
};
span_lint_and_then(
cx,
@@ -120,6 +99,19 @@ pub(super) fn check(
ex.span,
"map of a closure that does not depend on its parameter over a range",
|diag| {
// We need to provide nonempty parts to diag.multipart_suggestion so we
// collate all our parts here and then remove those that are empty.
let mut parts = vec![
(
ex.span.with_hi(method_name_span.hi()),
format!("{exec_context}::iter::{method_to_use_name}"),
),
new_span,
];
if use_take {
parts.push((ex.span.shrink_to_hi(), format!(".take({count})")));
}
diag.multipart_suggestion(
if use_take {
format!("remove the explicit range and use `{method_to_use_name}` and `take`")
@@ -89,3 +89,17 @@ fn msrv_1_82() {
std::iter::repeat(3).take(10);
//~^ map_with_unused_argument_over_ranges
}
fn wrongly_unmangled_macros() {
macro_rules! test {
($val:expr) => {
($val * 2 + 1)
};
}
std::iter::repeat_with(|| do_something()).take(test!(10));
//~^ map_with_unused_argument_over_ranges
std::iter::repeat_n(test!(3), 10);
//~^ map_with_unused_argument_over_ranges
}
@@ -89,3 +89,17 @@ fn msrv_1_82() {
(0..10).map(|_| 3);
//~^ map_with_unused_argument_over_ranges
}
fn wrongly_unmangled_macros() {
macro_rules! test {
($val:expr) => {
($val * 2 + 1)
};
}
(0..test!(10)).map(|_| do_something());
//~^ map_with_unused_argument_over_ranges
(0..10).map(|_| test!(3));
//~^ map_with_unused_argument_over_ranges
}
@@ -223,5 +223,29 @@ LL - (0..10).map(|_| 3);
LL + std::iter::repeat(3).take(10);
|
error: aborting due to 18 previous errors
error: map of a closure that does not depend on its parameter over a range
--> tests/ui/map_with_unused_argument_over_ranges.rs:100:5
|
LL | (0..test!(10)).map(|_| do_something());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove the explicit range and use `repeat_with` and `take`
|
LL - (0..test!(10)).map(|_| do_something());
LL + std::iter::repeat_with(|| do_something()).take(test!(10));
|
error: map of a closure that does not depend on its parameter over a range
--> tests/ui/map_with_unused_argument_over_ranges.rs:103:5
|
LL | (0..10).map(|_| test!(3));
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove the explicit range and use `repeat_n`
|
LL - (0..10).map(|_| test!(3));
LL + std::iter::repeat_n(test!(3), 10);
|
error: aborting due to 20 previous errors