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

new lint: unnecessary_reserve #14114

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

wowinter13
Copy link
Contributor

@wowinter13 wowinter13 commented Jan 30, 2025

resurrection of #10157

fixes #8982

changelog: [unnecessary_reserve]: add new lint unnecessary_reserve

@rustbot
Copy link
Collaborator

rustbot commented Jan 30, 2025

r? @Jarcho

rustbot has assigned @Jarcho.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Jan 30, 2025
@wowinter13 wowinter13 changed the title new lint: unnecessary_reserve new lint: unnecessary_reserve Jan 30, 2025
/// let array: &[usize] = &[1, 2];
/// vec.extend(array);
/// ```
#[clippy::version = "1.64.0"]
Copy link
Contributor

Choose a reason for hiding this comment

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

The current version is "1.86.0".

///
/// This lint checks for a call to `reserve` before `extend` on a `Vec` or `VecDeque`.
/// ### Why is this bad?
/// Since Rust 1.62, `extend` implicitly calls `reserve`
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you can omit "Since Rust 1.62", since it is more than two years old, while checking the MSRV is a good idea, talking about it in the description may not be necessary.

impl_lint_pass!(UnnecessaryReserve => [UNNECESSARY_RESERVE]);

pub struct UnnecessaryReserve {
msrv: Msrv,
Copy link
Contributor

@samueltardieu samueltardieu Jan 30, 2025

Choose a reason for hiding this comment

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

The lint also needs to be added to clippy_config/src/conf.rs.

/// ```
#[clippy::version = "1.64.0"]
pub UNNECESSARY_RESERVE,
pedantic,
Copy link
Contributor

Choose a reason for hiding this comment

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

Should it be pedantic or complexity?

Comment on lines +87 to +93
let acceptable_types = [sym::Vec, sym::VecDeque];
acceptable_types.iter().any(|&acceptable_ty| {
match cx.typeck_results().expr_ty(struct_calling_on).peel_refs().kind() {
ty::Adt(def, _) => cx.tcx.is_diagnostic_item(acceptable_ty, def.did()),
_ => false,
}
})
Copy link
Contributor

@samueltardieu samueltardieu Jan 30, 2025

Choose a reason for hiding this comment

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

cx.typeck_results() should be called once. Also, using expr_ty_adjusted() would let you handle cases where the Vec or VecDeque is behind a smart pointer such as Box (and you should add a test case for this).

I think you can match in a simpler way by:

  • Moving adt_def_id() from clippy_utils::ty::type_certainty to clippy_utils::ty, making it public and adding documentation ("Check if ty with references peeled is an Adt and return its DefId."). Fix the references by importing super::adt_def_id() in the type_certainty module.
  • Using adt_def_id() in your function, for example:
Suggested change
let acceptable_types = [sym::Vec, sym::VecDeque];
acceptable_types.iter().any(|&acceptable_ty| {
match cx.typeck_results().expr_ty(struct_calling_on).peel_refs().kind() {
ty::Adt(def, _) => cx.tcx.is_diagnostic_item(acceptable_ty, def.did()),
_ => false,
}
})
if let Some(did) = adt_def_id(cx.typeck_results().expr_ty_adjusted(struct_calling_on)) {
matches!(cx.tcx.get_diagnostic_name(did), Some(sym::Vec | sym::VecDeque))
} else {
false
}

struct_expr: &Expr<'tcx>,
args_a: &Expr<'tcx>,
) -> Option<rustc_span::Span> {
let args_a_kind = &args_a.kind;
Copy link
Contributor

Choose a reason for hiding this comment

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

It is possible to use args_a.kind directly at line 115, this is clearer and will not be more costly.

let mut read_found = false;
let mut spanless_eq = SpanlessEq::new(cx);

let _: Option<!> = for_each_expr(cx, block, |expr: &Expr<'tcx>| {
Copy link
Contributor

Choose a reason for hiding this comment

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

Right now, this will suggest to remove a call to reserve() which follows a call to extend() as well. It would be better to check only statements following the call to reserve(), as right now there is a risk of getting many false positives.

@wowinter13
Copy link
Contributor Author

@samueltardieu Samuel, really appreciate your review and points!
Will go through them tomorrow and make sure to apply the lessons

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unnecessary Vec/VecDeque reserve lint
4 participants