-
Notifications
You must be signed in to change notification settings - Fork 13.9k
Omit suggestions when spans are invalid #147849
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
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -304,19 +304,27 @@ fn as_substr<'a>(original: &'a str, suggestion: &'a str) -> Option<(usize, &'a s | |
} | ||
} | ||
|
||
/// Represents suggestions that were emitted because their span was ambiguous. | ||
pub(crate) struct SuggestionsOmitted(usize); | ||
|
||
impl CodeSuggestion { | ||
/// Returns the assembled code suggestions, whether they should be shown with an underline | ||
/// and whether the substitution only differs in capitalization. | ||
pub(crate) fn splice_lines( | ||
&self, | ||
sm: &SourceMap, | ||
) -> Vec<(String, Vec<SubstitutionPart>, Vec<Vec<SubstitutionHighlight>>, ConfusionType)> { | ||
) -> ( | ||
Vec<(String, Vec<SubstitutionPart>, Vec<Vec<SubstitutionHighlight>>, ConfusionType)>, | ||
SuggestionsOmitted, | ||
) { | ||
// For the `Vec<Vec<SubstitutionHighlight>>` value, the first level of the vector | ||
// corresponds to the output snippet's lines, while the second level corresponds to the | ||
// substrings within that line that should be highlighted. | ||
|
||
use rustc_span::{CharPos, Pos}; | ||
|
||
let num_omitted = Cell::new(0); | ||
|
||
/// Extracts a substring from the provided `line_opt` based on the specified low and high | ||
/// indices, appends it to the given buffer `buf`, and returns the count of newline | ||
/// characters in the substring for accurate highlighting. If `line_opt` is `None`, a | ||
|
@@ -365,13 +373,15 @@ impl CodeSuggestion { | |
|
||
assert!(!self.substitutions.is_empty()); | ||
|
||
self.substitutions | ||
let res = self | ||
.substitutions | ||
.iter() | ||
.filter(|subst| { | ||
// Suggestions coming from macros can have malformed spans. This is a heavy | ||
// handed approach to avoid ICEs by ignoring the suggestion outright. | ||
let invalid = subst.parts.iter().any(|item| sm.is_valid_span(item.span).is_err()); | ||
if invalid { | ||
num_omitted.update(|i| i + 1); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. some suggestions were already silently dropped, change this to count them |
||
debug!("splice_lines: suggestion contains an invalid span: {:?}", subst); | ||
} | ||
!invalid | ||
|
@@ -382,11 +392,16 @@ impl CodeSuggestion { | |
// are disjoint. Sort in ascending order. | ||
substitution.parts.sort_by_key(|part| part.span.lo()); | ||
// Verify the assumption that all spans are disjoint | ||
assert_eq!( | ||
substitution.parts.array_windows().find(|[a, b]| a.span.overlaps(b.span)), | ||
None, | ||
"all spans must be disjoint", | ||
); | ||
if substitution | ||
.parts | ||
.array_windows() | ||
.find(|[a, b]| a.span.overlaps(b.span)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. instead of failing an assertion, count how many times this happens and show this to the user |
||
.is_some() | ||
{ | ||
debug!("splice_lines: suggestion contains an invalid span: {substitution:?}"); | ||
num_omitted.update(|i| i + 1); | ||
return None; | ||
} | ||
|
||
// Account for cases where we are suggesting the same code that's already | ||
// there. This shouldn't happen often, but in some cases for multipart | ||
|
@@ -524,7 +539,9 @@ impl CodeSuggestion { | |
Some((buf, substitution.parts, highlights, confusion_type)) | ||
} | ||
}) | ||
.collect() | ||
.collect(); | ||
|
||
(res, SuggestionsOmitted(num_omitted.get())) | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -259,6 +259,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ | |
"run-pass", | ||
"run-rustfix", | ||
"rustc-env", | ||
"rustfix-dont-test-fixed", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: if we do go with this, can you please describe this new directive in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Absolutely |
||
"rustfix-only-machine-applicable", | ||
"should-fail", | ||
"should-ice", | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
extern crate proc_macro; | ||
use proc_macro::*; | ||
|
||
fn spans_callsite(ts: TokenStream) -> TokenStream { | ||
let mut new_ts = TokenStream::new(); | ||
|
||
for i in ts { | ||
let new_token = i.clone(); | ||
match new_token { | ||
TokenTree::Group(g) => { | ||
new_ts.extend([Group::new(g.delimiter(), spans_callsite(g.stream()))]) | ||
} | ||
mut other => { | ||
other.set_span(Span::call_site()); | ||
new_ts.extend([other]); | ||
} | ||
} | ||
} | ||
|
||
new_ts | ||
} | ||
|
||
#[proc_macro_attribute] | ||
pub fn all_spans_same(_: TokenStream, ts: TokenStream) -> TokenStream { | ||
spans_callsite(ts) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. recursively set all spans in the output to the callsite |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// regression test for #146808 | ||
//@ proc-macro: all_spans_same.rs | ||
//@ run-rustfix | ||
// the fixed program is still broken, but rustfix didn't crash! | ||
// that's what we want to test here. | ||
//@ rustfix-dont-test-fixed | ||
|
||
extern crate all_spans_same; | ||
|
||
#[all_spans_same::all_spans_same] | ||
//~^ ERROR wrong meta list delimiters | ||
#[allow{}] | ||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// regression test for #146808 | ||
//@ proc-macro: all_spans_same.rs | ||
//@ run-rustfix | ||
// the fixed program is still broken, but rustfix didn't crash! | ||
// that's what we want to test here. | ||
//@ rustfix-dont-test-fixed | ||
|
||
extern crate all_spans_same; | ||
|
||
#[all_spans_same::all_spans_same] | ||
//~^ ERROR wrong meta list delimiters | ||
#[allow{}] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. crashes, because we suggest changing {} to () but the suggestion consists of two parts |
||
fn main() {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
error: wrong meta list delimiters | ||
--> $DIR/invalid-delimeter-ice-146808.rs:10:1 | ||
| | ||
LL | #[all_spans_same::all_spans_same] | ||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ||
| | ||
= note: this error originates in the attribute macro `all_spans_same::all_spans_same` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
rendering error: omitted 1 suggestion that failed to render, likely because of macro expansions | ||
|
||
error: aborting due to 1 previous error | ||
|
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.
skip outputting suggestions with overlapping spans to JSON, same check as in lib.rs