1
Fork 0

correct suggestions in no_std (#13999)

I opened https://github.com/rust-lang/rust-clippy/pull/13896 before.
However, I found that there're more cases where Clippy suggests to use
modules that belong to the `std` crate even in a `no_std` environment.
Therefore, this PR include the changes I've made in #13896 and new
changes to fix cases I found this time to prevent wrong suggestions in
`no_std` environments as well.

changelog: [`redundant_closure`]: correct suggestion in `no_std`
changelog: [`repeat_vec_with_capacity`]: correct suggestion in `no_std`
changelog: [`single_range_in_vec_init`]: don't emit suggestion to use
`Vec` in `no_std`
changelog: [`drain_collect`]: correct suggestion in `no_std`
changelog: [`map_with_unused_argument_over_ranges`]: correct suggestion
in `no_std`

also close #13895
This commit is contained in:
Manish Goregaokar 2025-01-27 21:36:12 +00:00 committed by GitHub
commit 83bde363b6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 157 additions and 23 deletions

View file

@ -3,7 +3,9 @@ use clippy_utils::higher::VecArgs;
use clippy_utils::source::snippet_opt;
use clippy_utils::ty::get_type_diagnostic_name;
use clippy_utils::usage::{local_used_after_expr, local_used_in};
use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, path_to_local, path_to_local_id};
use clippy_utils::{
get_path_from_caller_to_method_type, is_adjusted, is_no_std_crate, path_to_local, path_to_local_id,
};
use rustc_errors::Applicability;
use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Safety, TyKind};
use rustc_infer::infer::TyCtxtInferExt;
@ -101,19 +103,20 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
};
if body.value.span.from_expansion() {
if body.params.is_empty() {
if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) {
// replace `|| vec![]` with `Vec::new`
span_lint_and_sugg(
cx,
REDUNDANT_CLOSURE,
expr.span,
"redundant closure",
"replace the closure with `Vec::new`",
"std::vec::Vec::new".into(),
Applicability::MachineApplicable,
);
}
if body.params.is_empty()
&& let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value)
{
let vec_crate = if is_no_std_crate(cx) { "alloc" } else { "std" };
// replace `|| vec![]` with `Vec::new`
span_lint_and_sugg(
cx,
REDUNDANT_CLOSURE,
expr.span,
"redundant closure",
"replace the closure with `Vec::new`",
format!("{vec_crate}::vec::Vec::new"),
Applicability::MachineApplicable,
);
}
// skip `foo(|| macro!())`
return;

View file

@ -1,8 +1,8 @@
use crate::methods::DRAIN_COLLECT;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_range_full;
use clippy_utils::source::snippet;
use clippy_utils::ty::is_type_lang_item;
use clippy_utils::{is_range_full, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath};
use rustc_lint::LateContext;
@ -58,12 +58,13 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, re
.then_some("Vec")
.or_else(|| check_string(cx, args, expr_ty, recv_ty_no_refs, recv_path).then_some("String"))
.or_else(|| check_collections(cx, expr_ty, recv_ty_no_refs))
&& let Some(exec_context) = std_or_core(cx)
{
let recv = snippet(cx, recv.span, "<expr>");
let sugg = if let ty::Ref(..) = recv_ty.kind() {
format!("std::mem::take({recv})")
format!("{exec_context}::mem::take({recv})")
} else {
format!("std::mem::take(&mut {recv})")
format!("{exec_context}::mem::take(&mut {recv})")
};
span_lint_and_sugg(

View file

@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::{eager_or_lazy, higher, usage};
use clippy_utils::{eager_or_lazy, higher, std_or_core, usage};
use rustc_ast::LitKind;
use rustc_ast::ast::RangeLimits;
use rustc_data_structures::packed::Pu128;
@ -75,6 +75,7 @@ pub(super) fn check(
} = body_hir
&& !usage::BindingUsageFinder::are_params_used(cx, body_hir)
&& let Some(count) = extract_count_with_applicability(cx, range, &mut applicability)
&& let Some(exec_context) = std_or_core(cx)
{
let method_to_use_name;
let new_span;
@ -105,7 +106,7 @@ pub(super) fn check(
let mut parts = vec![
(
receiver.span.to(method_call_span),
format!("std::iter::{method_to_use_name}"),
format!("{exec_context}::iter::{method_to_use_name}"),
),
new_span,
];

View file

@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::VecArgs;
use clippy_utils::macros::matching_root_macro_call;
use clippy_utils::source::snippet;
use clippy_utils::{expr_or_init, fn_def_id};
use clippy_utils::{expr_or_init, fn_def_id, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
@ -93,6 +93,7 @@ fn check_repeat_fn(cx: &LateContext<'_>, expr: &Expr<'_>) {
&& let ExprKind::Call(_, [repeat_expr]) = expr.kind
&& fn_def_id(cx, repeat_expr).is_some_and(|did| cx.tcx.is_diagnostic_item(sym::vec_with_capacity, did))
&& !repeat_expr.span.from_expansion()
&& let Some(exec_context) = std_or_core(cx)
{
emit_lint(
cx,
@ -100,7 +101,10 @@ fn check_repeat_fn(cx: &LateContext<'_>, expr: &Expr<'_>) {
"iter::repeat",
"none of the yielded `Vec`s will have the requested capacity",
"if you intended to create an iterator that yields `Vec`s with an initial capacity, try",
format!("std::iter::repeat_with(|| {})", snippet(cx, repeat_expr.span, "..")),
format!(
"{exec_context}::iter::repeat_with(|| {})",
snippet(cx, repeat_expr.span, "..")
),
);
}
}

View file

@ -1,9 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::get_trait_def_id;
use clippy_utils::higher::VecArgs;
use clippy_utils::macros::root_macro_call_first_node;
use clippy_utils::source::SpanRangeExt;
use clippy_utils::ty::implements_trait;
use clippy_utils::{get_trait_def_id, is_no_std_crate};
use rustc_ast::{LitIntType, LitKind, UintTy};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr};
@ -125,7 +125,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
span,
format!("{suggested_type} of `Range` that is only one element"),
|diag| {
if should_emit_every_value {
if should_emit_every_value && !is_no_std_crate(cx) {
diag.span_suggestion(
span,
"if you wanted a `Vec` that contains the entire range, try",

View file

@ -0,0 +1,8 @@
#![warn(clippy::drain_collect)]
#![no_std]
extern crate alloc;
use alloc::vec::Vec;
fn remove_all(v: &mut Vec<i32>) -> Vec<i32> {
core::mem::take(v)
}

View file

@ -0,0 +1,8 @@
#![warn(clippy::drain_collect)]
#![no_std]
extern crate alloc;
use alloc::vec::Vec;
fn remove_all(v: &mut Vec<i32>) -> Vec<i32> {
v.drain(..).collect()
}

View file

@ -0,0 +1,11 @@
error: you seem to be trying to move all elements into a new `Vec`
--> tests/ui/drain_collect_nostd.rs:7:5
|
LL | v.drain(..).collect()
| ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `core::mem::take(v)`
|
= note: `-D clippy::drain-collect` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::drain_collect)]`
error: aborting due to 1 previous error

10
tests/ui/eta_nostd.fixed Normal file
View file

@ -0,0 +1,10 @@
#![warn(clippy::redundant_closure)]
#![no_std]
extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;
fn issue_13895() {
let _: Option<Vec<u8>> = true.then(alloc::vec::Vec::new);
}

10
tests/ui/eta_nostd.rs Normal file
View file

@ -0,0 +1,10 @@
#![warn(clippy::redundant_closure)]
#![no_std]
extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;
fn issue_13895() {
let _: Option<Vec<u8>> = true.then(|| vec![]);
}

11
tests/ui/eta_nostd.stderr Normal file
View file

@ -0,0 +1,11 @@
error: redundant closure
--> tests/ui/eta_nostd.rs:9:40
|
LL | let _: Option<Vec<u8>> = true.then(|| vec![]);
| ^^^^^^^^^ help: replace the closure with `Vec::new`: `alloc::vec::Vec::new`
|
= note: `-D clippy::redundant-closure` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]`
error: aborting due to 1 previous error

View file

@ -0,0 +1,8 @@
#![warn(clippy::map_with_unused_argument_over_ranges)]
#![no_std]
extern crate alloc;
use alloc::vec::Vec;
fn nostd(v: &mut [i32]) {
let _: Vec<_> = core::iter::repeat_n(3 + 1, 10).collect();
}

View file

@ -0,0 +1,8 @@
#![warn(clippy::map_with_unused_argument_over_ranges)]
#![no_std]
extern crate alloc;
use alloc::vec::Vec;
fn nostd(v: &mut [i32]) {
let _: Vec<_> = (0..10).map(|_| 3 + 1).collect();
}

View file

@ -0,0 +1,15 @@
error: map of a closure that does not depend on its parameter over a range
--> tests/ui/map_with_unused_argument_over_ranges_nostd.rs:7:21
|
LL | let _: Vec<_> = (0..10).map(|_| 3 + 1).collect();
| ^^^^^^^^^^^^^^^^^^^^^^
|
= note: `-D clippy::map-with-unused-argument-over-ranges` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::map_with_unused_argument_over_ranges)]`
help: remove the explicit range and use `repeat_n`
|
LL | let _: Vec<_> = core::iter::repeat_n(3 + 1, 10).collect();
| ~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~
error: aborting due to 1 previous error

View file

@ -0,0 +1,10 @@
#![warn(clippy::repeat_vec_with_capacity)]
#![allow(clippy::manual_repeat_n)]
#![no_std]
use core::iter;
extern crate alloc;
use alloc::vec::Vec;
fn nostd() {
let _: Vec<Vec<u8>> = core::iter::repeat_with(|| Vec::with_capacity(42)).take(123).collect();
}

View file

@ -0,0 +1,10 @@
#![warn(clippy::repeat_vec_with_capacity)]
#![allow(clippy::manual_repeat_n)]
#![no_std]
use core::iter;
extern crate alloc;
use alloc::vec::Vec;
fn nostd() {
let _: Vec<Vec<u8>> = iter::repeat(Vec::with_capacity(42)).take(123).collect();
}

View file

@ -0,0 +1,16 @@
error: repeating `Vec::with_capacity` using `iter::repeat`, which does not retain capacity
--> tests/ui/repeat_vec_with_capacity_nostd.rs:9:27
|
LL | let _: Vec<Vec<u8>> = iter::repeat(Vec::with_capacity(42)).take(123).collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: none of the yielded `Vec`s will have the requested capacity
= note: `-D clippy::repeat-vec-with-capacity` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::repeat_vec_with_capacity)]`
help: if you intended to create an iterator that yields `Vec`s with an initial capacity, try
|
LL | let _: Vec<Vec<u8>> = core::iter::repeat_with(|| Vec::with_capacity(42)).take(123).collect();
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to 1 previous error