-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Remove overlapping parts of multipart suggestions #106916
Conversation
(rustbot has picked a reviewer for you, use r? to override) |
for (index, part_outer) in parts.iter().enumerate() { | ||
debug_assert!( | ||
!part_outer.span.is_empty() || !part_outer.snippet.is_empty(), | ||
"Span must not be empty and have no suggestion", | ||
); | ||
for part_inner in parts.iter().skip(index + 1) { | ||
debug_assert!( | ||
!part_outer.span.overlaps(part_inner.span), | ||
"suggestion must not have overlapping parts:\n{part_outer:?}\n{part_inner:?}", | ||
); | ||
} | ||
} |
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.
Does it make sense to sort parts by the span
? this way you only need to check the two adjacent ones. But, we shouldn't do this just for the debug assertions maybe...
Either way, I think it would be better to enclose this in if cfg!(debug_assertions) {}
, so llvm has an easier time removing the useless loops.
Also, can we somehow test this assertion?
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 think it is perfectly safe to always unconditionally sort here.
r? @estebank |
for (index, part_outer) in parts.iter().enumerate() { | ||
debug_assert!( | ||
!part_outer.span.is_empty() || !part_outer.snippet.is_empty(), | ||
"Span must not be empty and have no suggestion", | ||
); | ||
for part_inner in parts.iter().skip(index + 1) { | ||
debug_assert!( | ||
!part_outer.span.overlaps(part_inner.span), | ||
"suggestion must not have overlapping parts:\n{part_outer:?}\n{part_inner:?}", | ||
); | ||
} | ||
} |
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.
After sorting suggestion:
for (index, part_outer) in parts.iter().enumerate() { | |
debug_assert!( | |
!part_outer.span.is_empty() || !part_outer.snippet.is_empty(), | |
"Span must not be empty and have no suggestion", | |
); | |
for part_inner in parts.iter().skip(index + 1) { | |
debug_assert!( | |
!part_outer.span.overlaps(part_inner.span), | |
"suggestion must not have overlapping parts:\n{part_outer:?}\n{part_inner:?}", | |
); | |
} | |
} | |
let mut parts = parts.iter.peekable(); | |
while let Some(part) = parts.next() { | |
debug_assert!( | |
!partspan.is_empty() || !part.snippet.is_empty(), | |
"Span must not be empty and have no suggestion", | |
); | |
let Some(next) = parts.peek() else { break; } | |
debug_assert!( | |
!part.span.overlaps(next.span), | |
"suggestion must not have overlapping parts:\n{part:?}\n{next:?}", | |
); | |
} |
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 think this can be written nicer with parts.array_windows()
. Something like
debug_assert!(parts.iter().all(|part| !part.span.is_empty() || !part.snipper.is_empty()), "Span must not be empty and have no suggestion");
parts.array_windows(|[a, b]| debug_assert!(!a.span.overlaps(b.span), "suggestion must not have overlapping parts:\n{a:?}\n{b:?}"));
let rhs_tt = &rhs.tts[i]; | ||
let ctxt = tt.span().ctxt(); | ||
match (&mut tt, rhs_tt) { | ||
// preserve the delim spans if able |
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.
Nice
r=me after addressing the sorting/assertion nitpick |
b56c49a
to
d18768a
Compare
@bors r=estebank |
For the record, this is how this assertion looks now:
|
…stebank Remove overlapping parts of multipart suggestions This PR adds a debug assertion that the parts of a single substitution cannot overlap, fixes a overlapping substitution from the testsuite, and fixes rust-lang#106870. Note that a single suggestion can still have multiple overlapping substitutions / possible edits, we just don't suggest overlapping replacements in a single edit anymore. I've also included a fix for an unrelated bug where rustfix for `explicit_outlives_requirements` would produce multiple trailing commas for a where clause.
Might break clippy tests |
It does look like it breaks clippy tests (diff from #107020): -error: variables can be used directly in the `format!` string
- |
- |
-LL | println!("{0} {0}", local_i32);
- |
-help: change this to
- |
- |
-LL - println!("{0} {0}", local_i32);
-LL + println!("{local_i32} {local_i32}");
+thread 'rustc' panicked at 'assertion failed: `(left == right)`
+ left: `None`,
+ left: `None`,
+ right: `Some([SubstitutionPart { span: $DIR/uninlined_format_args.rs:78:23: 78:34 (#0), snippet: "" }, SubstitutionPart { span: $DIR/uninlined_format_args.rs:78:23: 78:34 (#0), snippet: "" }])`: suggestion must not have overlapping parts', /checkout/compiler/rustc_errors/src/diagnostic.rs:646:9 Not sure why clippy removes the same span twice though 🤔 |
@bors r- |
d18768a
to
17a9aa9
Compare
Some changes occurred in src/tools/clippy cc @rust-lang/clippy |
I fixed the clippy failure and also changed the signature of @rustbot ready |
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.
Generally it's better to make smaller PR and separate refactorings, but this looks good overall, except for a couple of nitpicks.
let sugg = | ||
trait_should_be_self.iter().map(|span| (*span, "Self".to_string())).collect::<Vec<_>>(); |
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.
Wouldn't it make more sense to remove the collect altogether?
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.
This vector is moved into a MultiSpan
below. Alternatively this can be .clone().into_iter().map()
instead of .iter().map().collect()
, but I don't know if that's any better?
compiler/rustc_lint/src/builtin.rs
Outdated
let lint_spans = lint_spans.into_iter().collect::<Vec<_>>(); | ||
|
||
cx.emit_spanned_lint( | ||
EXPLICIT_OUTLIVES_REQUIREMENTS, | ||
lint_spans.clone(), |
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.
Remove collect
? also the clone seems unnecessary? Can't this just be
let lint_spans = lint_spans.into_iter().collect::<Vec<_>>(); | |
cx.emit_spanned_lint( | |
EXPLICIT_OUTLIVES_REQUIREMENTS, | |
lint_spans.clone(), | |
cx.emit_spanned_lint( | |
EXPLICIT_OUTLIVES_REQUIREMENTS, | |
lint_spans, |
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.
The clone is necessary, because we need one vector for the MultiSpan
of the lint itself and one for the suggestion.
The collect is turning the HashSet
into a Vec
for the MultiSpan
. But it would probably be better to just sort and dedup a vector instead of using a hash set, will change that.
.map(|(span, snippet)| SubstitutionPart { snippet, span }) | ||
.collect(), | ||
}], | ||
substitutions: vec![Substitution { parts }], |
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.
💭 would small vec be good here?
I assume you've removed the generic changes, so let's see what perf says @bors try @rust-timer queue |
This comment has been minimized.
This comment has been minimized.
⌛ Trying commit 228ddf0 with merge 5bfca626acb506f9da84b4eac21a87e0847f8785... |
@bors try |
⌛ Trying commit 228ddf0 with merge cddc29a75100f2566a46e957e0b0df708bc12c68... |
☀️ Try build successful - checks-actions |
1 similar comment
☀️ Try build successful - checks-actions |
This comment has been minimized.
This comment has been minimized.
Finished benchmarking commit (cddc29a75100f2566a46e957e0b0df708bc12c68): comparison URL. Overall result: ❌ regressions - no action neededBenchmarking this pull request likely means that it is perf-sensitive, so we're automatically marking it as not fit for rolling up. While you can manually mark this PR as fit for rollup, we strongly recommend not doing so since this PR may lead to changes in compiler perf. @bors rollup=never Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)ResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesThis benchmark run did not return any relevant results for this metric. |
Regression is from the improvements in #107343 (comment), so probably noise. |
@bors r=estebank |
☀️ Test successful - checks-actions |
Finished benchmarking commit (d8da513): comparison URL. Overall result: ✅ improvements - no action needed@rustbot label: -perf-regression Instruction countThis is a highly reliable metric that was used to determine the overall result at the top of this comment.
Max RSS (memory usage)ResultsThis is a less reliable metric that may be of interest but was not used to determine the overall result at the top of this comment.
CyclesThis benchmark run did not return any relevant results for this metric. |
@lukas-code do you still have the commit/diff of making diagnostic functions accept |
@WaffleLapkin I pulled this out of my reflog: 36d9953 https://github.com/lukas-code/rust/tree/multipart-impl-iterator |
@lukas-code thanks! |
…ebank Remove overlapping parts of multipart suggestions This PR adds a debug assertion that the parts of a single substitution cannot overlap, fixes a overlapping substitution from the testsuite, and fixes rust-lang#106870. Note that a single suggestion can still have multiple overlapping substitutions / possible edits, we just don't suggest overlapping replacements in a single edit anymore. I've also included a fix for an unrelated bug where rustfix for `explicit_outlives_requirements` would produce multiple trailing commas for a where clause.
This PR adds a debug assertion that the parts of a single substitution cannot overlap, fixes a overlapping substitution from the testsuite, and fixes #106870.
Note that a single suggestion can still have multiple overlapping substitutions / possible edits, we just don't suggest overlapping replacements in a single edit anymore.
I've also included a fix for an unrelated bug where rustfix for
explicit_outlives_requirements
would produce multiple trailing commas for a where clause.