Skip to content

Useless vec false positive #11895

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

Merged
merged 1 commit into from
Dec 12, 2023
Merged

Useless vec false positive #11895

merged 1 commit into from
Dec 12, 2023

Conversation

ericwu17
Copy link
Contributor

changelog: [useless_vec]: fix false positive in macros.

fixes #11861

We delay the emission of useless_vec lints to the check_crate_post stage, which allows us to effectively undo lints if we find that a vec![] expression is being used multiple times after macro expansion.

@rustbot
Copy link
Collaborator

rustbot commented Nov 29, 2023

r? @blyxyas

(rustbot has picked a reviewer for you, use r? to override)

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Nov 29, 2023
@@ -20,6 +21,8 @@ use rustc_span::{sym, Span};
pub struct UselessVec {
pub too_large_for_stack: u64,
pub msrv: Msrv,
pub span_to_lint_map: BTreeMap<Span, Option<(HirId, SuggestedType, String, Applicability)>>,
pub spans_sure_to_lint: BTreeSet<Span>,
Copy link
Member

Choose a reason for hiding this comment

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

Is this extra set needed? It seems to me like the map on its own would be enough for determining uses

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have removed this set, now there's only the span_to_lint_map.

SuggestedType::Array,
);
let span = expr.span.ctxt().outer_expn_data().call_site;
self.spans_sure_to_lint.insert(span);
Copy link
Member

Choose a reason for hiding this comment

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

Is the logic here that if all uses of the variable let x = vec![]; adjust to a slice, then this is guaranteed to not be from a macro argument and we can always lint?

That would break down here I think:

macro_rules! m {
  ($x:expr) => {
    fn f() { let _x = $x; $x.starts_with(&[]); }
    fn f2() { let _x: Vec<i32> = $x; }
  }
}
m!(vec![1]);

If I'm reading this correctly, it would lint here.
All uses of _x in f adjust to a slice, but it's still expanded twice.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, thanks for the catch. You are correct that the logic is broken here. My intention of adding the spans_sure_to_lint set was that when sometimes we might want to lint for _ in vec![...], but since there the vec![...] does not adjust to a slice, subseqeunt check_expr calls would insert None into the span_to_lint_map.

I think I will change the logic to store a set of HirIds that are ok to lint, since expressions after macro expansion will have distinct HirIds.

Copy link
Member

@blyxyas blyxyas left a comment

Choose a reason for hiding this comment

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

Meow :3

self.check_vec_macro(cx, &vec_args, span, arg.hir_id, SuggestedType::Array);
}
// search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]`
else if let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) {
Copy link
Member

Choose a reason for hiding this comment

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

The first check of this method is already searching for VecArgs::hir(cx, expr), is this check necessary again?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The first check of this method was specifically looking for cases of let x = vec![...] where every subsequent use of x adjusts to a slice, whereas this check looks for cases where vec![] is used in a context where the expression's type adjusts to a slice.

The original lint was also set up in this manner, but I readjusted the order of the different checks and also changed subsequent checks to use "else if" instead of "if".

Copy link
Member

Choose a reason for hiding this comment

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

But I don't understand why you chose to create a new branch checking if let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()), instead of adding this logic to the previous that checks the same thing.


fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
for (span, lint_opt) in &self.span_to_lint_map {
if let Some((hir_id, suggest_slice, snippet, applicability)) = lint_opt {
Copy link
Member

Choose a reason for hiding this comment

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

This if statement means that in cases like that self.span_to_lint_map.insert(span, None); in line 129, it will not lint. In that case, why insert it in the first case?

If not linting is intended, both that line and the whole lintable_exprs field can be removed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The reason to insert None when we don't want to lint is to mark the specific span as non-lintable. There are cases where the same span expands to multiple expressions (when a macro expands the vec!) and we want to lint only when all of the expanded expressions are ok for linting.

The reason for lintable_exprs is because the first two checks of the lint will check cases where we do want to emit the lint even when the vec![...] expression doesn't adjust to a slice. I chose to keep track of those expressions separately, although I could have also added a parameter to check_vec_macro indicating whether we want to lint immediately, or wait until the check_crate_post stage to emit lints.

Copy link
Member

Choose a reason for hiding this comment

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

There's no need to gather all lintable expressions in a single collection and then do a crate pass, you can just lint in place.

The infrastructure needed to collect all lintable expressions costs performance and memory, some things that we're trying to improve right now. If you're trying to improve code cleanliness, you can just refactor the span_lint... function into it's own function that takes less arguments. Like a report function or emit. It's done in other lints to avoid typing the same description / notes multiple times.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion. I'll look into refactoring this weekend, since I am busy with school this week.

Copy link
Member

@blyxyas blyxyas left a comment

Choose a reason for hiding this comment

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

Welp 。^・ェ・^。

I pulled this PR on my personal computer to check if I could remove that new field, to no avail. I think that the current state of the PR is good to be merged, although I'd love to not have to add a new field / have to mark some spans as unlintable.

@y21 Do you find a way to remove that new field, or do you approve?
I'll approve from my part, let's see if someone comes before this Wednesday that has some ideas.

@blyxyas
Copy link
Member

blyxyas commented Dec 12, 2023

Eric, it seems like it will continue as-is, this is just the best solution we could find. Could you squash these commits into a single one?

this fixes issue rust-lang#11861 by adding an extra map to
keep track of which spans are ok to lint
@blyxyas
Copy link
Member

blyxyas commented Dec 12, 2023

@bors r+

@bors
Copy link
Contributor

bors commented Dec 12, 2023

📌 Commit 884bec3 has been approved by blyxyas

It is now in the queue for this repository.

@bors
Copy link
Contributor

bors commented Dec 12, 2023

⌛ Testing commit 884bec3 with merge c19508b...

@bors
Copy link
Contributor

bors commented Dec 12, 2023

☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test
Approved by: blyxyas
Pushing c19508b to master...

@bors bors merged commit c19508b into rust-lang:master Dec 12, 2023
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.

False positive useless_vec when vec argument is used twice in a macro
5 participants