-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Add duplicate trait bound lint #95052
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
Conversation
r? @lcnr (rust-highfive has picked a reviewer for you, use r? to override) |
@rustbot author |
compiler/rustc_lint/src/traits.rs
Outdated
|
||
for bound in bounds_vec { | ||
// LOGIC GOES HERE | ||
if bounds_vec.contains(&bound) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two things here:
- Maybe we can use a
pop
orremove
to check for duplicates in a loop like this? - What's the best way to do the equality check given a
PolyTraitRef
? Not a lot ofEq
implementations on the types here...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe we can dig all the way in to the corresponding DefId
of the trait and use that, so we skip the ones that aren't trait bounds (like : 'a
and <X as Y>::Z: A
) and ignore the others. It'd be nice to also find redundant bounds for those, but we can start with only trait bounds.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
af48e19
to
0beecd0
Compare
This comment has been minimized.
This comment has been minimized.
0beecd0
to
0dea762
Compare
This comment has been minimized.
This comment has been minimized.
0dea762
to
ab03610
Compare
This comment has been minimized.
This comment has been minimized.
Can you add a test exercising this? |
This comment has been minimized.
This comment has been minimized.
When adding a test file you need to 1) annotate the line with an error with |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
b050abd
to
bc0bb88
Compare
@rustbot ready |
Co-authored-by: Esteban Kuber <[email protected]>
bc0bb88
to
416195e
Compare
This comment has been minimized.
This comment has been minimized.
@rustbot author |
Looks like we need to inspect the generics as well... |
This comment has been minimized.
This comment has been minimized.
Co-authored-by: Esteban Kuber <[email protected]>
let Some(did) = polytraitref.trait_ref.trait_def_id() else { continue; }; | ||
// If inserting the trait bound into the set returns `false`, | ||
// there is a duplicate. | ||
if !set.insert(did) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of using a set, you should use a FxHashMap so that you can keep the Span around and use that Span in the lint to point at the previous trait bound, to minimize confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this!
I'm not sure which methods to use here. Using this rough implementation
let mut map = FxHashMap::default();
for bound in bounds.into_iter() {
match bound {
GenericBound::Trait(polytraitref, _) => {
let Some(did) = polytraitref.trait_ref.trait_def_id() else { continue; };
let span = polytraitref.span;
if map.insert(&did, &span).is_none() {
let span_of_dup_bound = span;
let span_of_prev_bound = map.get(&did).unwrap();
cx.struct_span_lint(DUP_TRAIT_BOUNDS, span_of_dup_bound, |lint| {
let msg = format!("duplicate trait bound");
lint.build(&msg)
.span_note(span_of_prev_bound, "trait bound previously used here")
.span_help(span_of_dup_bound, "remove this duplicate trait bound")
.emit();
});
};
}
_ => continue,
}
}
}
I'm getting
--> compiler/rustc_lint/src/traits.rs:169:44
|
169 | ... .span_help(span_of_prev_bound, "trait bound previously used here")
| --------- ^^^^^^^^^^^^^^^^^^ the trait `From<&&rustc_span::Span>` is not implemented for `MultiSpan`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `From<T>`:
<MultiSpan as From<Vec<rustc_span::Span>>>
<MultiSpan as From<rustc_span::Span>>
= note: required because of the requirements on the impl of `Into<MultiSpan>` for `&&rustc_span::Span`
note: required by a bound in `DiagnosticBuilder::<'a, G>::span_help`
--> /Users/will/repos/rust/compiler/rustc_errors/src/diagnostic_builder.rs:457:18
|
457 | sp: impl Into<MultiSpan>,
| ^^^^^^^^^^^^^^^ required by this bound in `DiagnosticBuilder::<'a, G>::span_help`
I don't yet understand how to work with MultiSpan
...
Also, for right now I'm thinking of this span message as a nice-to-have. I'm more worried about this test case and handling the generic parameters:
trait StoreIndex: Indexer<u8> + Indexer<u16> {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is because you have a &&Span
and you need to dereference it to work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is because you have a &&Span and you need to dereference it to work.
Fun case, because then we should be suggesting the derefecence in the diagnostic, and we're not :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can use try_insert
:
let mut map = FxHashMap::default();
for bound in bounds.into_iter() {
match bound {
GenericBound::Trait(polytraitref, _) => {
let Some(did) = polytraitref.trait_ref.trait_def_id() else { continue; };
let span = polytraitref.span;
if let Err(occupied) = map.try_insert(did, span) {
cx.struct_span_lint(DUP_TRAIT_BOUNDS, span, |lint| {
let msg = format!("duplicate trait bound");
lint.build(&msg)
.span_label(*occupied.value, "trait bound previously used here")
.span_label(span, "duplicate trait bound")
.emit();
});
};
}
_ => continue,
}
}
This comment has been minimized.
This comment has been minimized.
@pierwill any updates on the test failure? |
☔ The latest upstream changes (presumably #104863) made this pull request unmergeable. Please resolve the merge conflicts. |
Closing this as inactive. |
Do not allow duplicate trait bounds.
Closes #90108.