Skip to content

Commit e901380

Browse files
authored
Rollup merge of rust-lang#137348 - compiler-errors:span-trim, r=estebank
More sophisticated span trimming for suggestions Previously rust-lang#136958 only cared about prefixes or suffixes. Now it detects more cases where a suggestion is "sandwiched" by unchanged code on the left or the right. Would be cool if we could detect several insertions, like `ACE` going to `ABCDE`, extracting `B` and `D`, but that seems unwieldy. r? ``@estebank``
2 parents ed98e56 + 160905b commit e901380

File tree

133 files changed

+1036
-1247
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

133 files changed

+1036
-1247
lines changed

compiler/rustc_errors/src/emitter.rs

+1-6
Original file line numberDiff line numberDiff line change
@@ -2216,12 +2216,7 @@ impl HumanEmitter {
22162216
if let DisplaySuggestion::Diff | DisplaySuggestion::Underline | DisplaySuggestion::Add =
22172217
show_code_change
22182218
{
2219-
for mut part in parts {
2220-
// If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the
2221-
// suggestion and snippet to look as if we just suggested to add
2222-
// `"b"`, which is typically much easier for the user to understand.
2223-
part.trim_trivial_replacements(sm);
2224-
2219+
for part in parts {
22252220
let snippet = if let Ok(snippet) = sm.span_to_snippet(part.span) {
22262221
snippet
22272222
} else {

compiler/rustc_errors/src/lib.rs

+40-12
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ use rustc_macros::{Decodable, Encodable};
7171
pub use rustc_span::ErrorGuaranteed;
7272
pub use rustc_span::fatal_error::{FatalError, FatalErrorMarker};
7373
use rustc_span::source_map::SourceMap;
74-
use rustc_span::{DUMMY_SP, Loc, Span};
74+
use rustc_span::{BytePos, DUMMY_SP, Loc, Span};
7575
pub use snippet::Style;
7676
// Used by external projects such as `rust-gpu`.
7777
// See https://github.com/rust-lang/rust/pull/115393.
@@ -237,10 +237,9 @@ impl SubstitutionPart {
237237
/// it with "abx" is, since the "c" character is lost.
238238
pub fn is_destructive_replacement(&self, sm: &SourceMap) -> bool {
239239
self.is_replacement(sm)
240-
&& !sm.span_to_snippet(self.span).is_ok_and(|snippet| {
241-
self.snippet.trim_start().starts_with(snippet.trim_start())
242-
|| self.snippet.trim_end().ends_with(snippet.trim_end())
243-
})
240+
&& !sm
241+
.span_to_snippet(self.span)
242+
.is_ok_and(|snippet| as_substr(snippet.trim(), self.snippet.trim()).is_some())
244243
}
245244

246245
fn replaces_meaningful_content(&self, sm: &SourceMap) -> bool {
@@ -257,16 +256,40 @@ impl SubstitutionPart {
257256
let Ok(snippet) = sm.span_to_snippet(self.span) else {
258257
return;
259258
};
260-
if self.snippet.starts_with(&snippet) {
261-
self.span = self.span.shrink_to_hi();
262-
self.snippet = self.snippet[snippet.len()..].to_string();
263-
} else if self.snippet.ends_with(&snippet) {
264-
self.span = self.span.shrink_to_lo();
265-
self.snippet = self.snippet[..self.snippet.len() - snippet.len()].to_string();
259+
260+
if let Some((prefix, substr, suffix)) = as_substr(&snippet, &self.snippet) {
261+
self.span = Span::new(
262+
self.span.lo() + BytePos(prefix as u32),
263+
self.span.hi() - BytePos(suffix as u32),
264+
self.span.ctxt(),
265+
self.span.parent(),
266+
);
267+
self.snippet = substr.to_string();
266268
}
267269
}
268270
}
269271

272+
/// Given an original string like `AACC`, and a suggestion like `AABBCC`, try to detect
273+
/// the case where a substring of the suggestion is "sandwiched" in the original, like
274+
/// `BB` is. Return the length of the prefix, the "trimmed" suggestion, and the length
275+
/// of the suffix.
276+
fn as_substr<'a>(original: &'a str, suggestion: &'a str) -> Option<(usize, &'a str, usize)> {
277+
let common_prefix = original
278+
.chars()
279+
.zip(suggestion.chars())
280+
.take_while(|(c1, c2)| c1 == c2)
281+
.map(|(c, _)| c.len_utf8())
282+
.sum();
283+
let original = &original[common_prefix..];
284+
let suggestion = &suggestion[common_prefix..];
285+
if suggestion.ends_with(original) {
286+
let common_suffix = original.len();
287+
Some((common_prefix, &suggestion[..suggestion.len() - original.len()], common_suffix))
288+
} else {
289+
None
290+
}
291+
}
292+
270293
impl CodeSuggestion {
271294
/// Returns the assembled code suggestions, whether they should be shown with an underline
272295
/// and whether the substitution only differs in capitalization.
@@ -380,7 +403,12 @@ impl CodeSuggestion {
380403
// or deleted code in order to point at the correct column *after* substitution.
381404
let mut acc = 0;
382405
let mut only_capitalization = false;
383-
for part in &substitution.parts {
406+
for part in &mut substitution.parts {
407+
// If this is a replacement of, e.g. `"a"` into `"ab"`, adjust the
408+
// suggestion and snippet to look as if we just suggested to add
409+
// `"b"`, which is typically much easier for the user to understand.
410+
part.trim_trivial_replacements(sm);
411+
384412
only_capitalization |= is_case_difference(sm, &part.snippet, part.span);
385413
let cur_lo = sm.lookup_char_pos(part.span.lo());
386414
if prev_hi.line == cur_lo.line {

src/tools/clippy/tests/ui/async_yields_async.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ LL | | };
1414
= help: to override `-D warnings` add `#[allow(clippy::async_yields_async)]`
1515
help: consider awaiting this value
1616
|
17-
LL ~ async {
18-
LL + 3
19-
LL + }.await
17+
LL | async {
18+
LL | 3
19+
LL ~ }.await
2020
|
2121

2222
error: an async construct yields a type which is itself awaitable
@@ -46,9 +46,9 @@ LL | | };
4646
|
4747
help: consider awaiting this value
4848
|
49-
LL ~ async {
50-
LL + 3
51-
LL + }.await
49+
LL | async {
50+
LL | 3
51+
LL ~ }.await
5252
|
5353

5454
error: an async construct yields a type which is itself awaitable

src/tools/clippy/tests/ui/borrow_deref_ref_unfixable.stderr

+2-3
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,8 @@ LL + let x: &str = s;
1313
|
1414
help: if you would like to deref, try using `&**`
1515
|
16-
LL - let x: &str = &*s;
17-
LL + let x: &str = &**s;
18-
|
16+
LL | let x: &str = &**s;
17+
| +
1918

2019
error: aborting due to 1 previous error
2120

src/tools/clippy/tests/ui/fn_to_numeric_cast_any.stderr

+34-51
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,8 @@ LL | let _ = foo as i8;
88
= help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast_any)]`
99
help: did you mean to invoke the function?
1010
|
11-
LL - let _ = foo as i8;
12-
LL + let _ = foo() as i8;
13-
|
11+
LL | let _ = foo() as i8;
12+
| ++
1413

1514
error: casting function pointer `foo` to `i16`
1615
--> tests/ui/fn_to_numeric_cast_any.rs:26:13
@@ -20,9 +19,8 @@ LL | let _ = foo as i16;
2019
|
2120
help: did you mean to invoke the function?
2221
|
23-
LL - let _ = foo as i16;
24-
LL + let _ = foo() as i16;
25-
|
22+
LL | let _ = foo() as i16;
23+
| ++
2624

2725
error: casting function pointer `foo` to `i32`
2826
--> tests/ui/fn_to_numeric_cast_any.rs:28:13
@@ -32,9 +30,8 @@ LL | let _ = foo as i32;
3230
|
3331
help: did you mean to invoke the function?
3432
|
35-
LL - let _ = foo as i32;
36-
LL + let _ = foo() as i32;
37-
|
33+
LL | let _ = foo() as i32;
34+
| ++
3835

3936
error: casting function pointer `foo` to `i64`
4037
--> tests/ui/fn_to_numeric_cast_any.rs:30:13
@@ -44,9 +41,8 @@ LL | let _ = foo as i64;
4441
|
4542
help: did you mean to invoke the function?
4643
|
47-
LL - let _ = foo as i64;
48-
LL + let _ = foo() as i64;
49-
|
44+
LL | let _ = foo() as i64;
45+
| ++
5046

5147
error: casting function pointer `foo` to `i128`
5248
--> tests/ui/fn_to_numeric_cast_any.rs:32:13
@@ -56,9 +52,8 @@ LL | let _ = foo as i128;
5652
|
5753
help: did you mean to invoke the function?
5854
|
59-
LL - let _ = foo as i128;
60-
LL + let _ = foo() as i128;
61-
|
55+
LL | let _ = foo() as i128;
56+
| ++
6257

6358
error: casting function pointer `foo` to `isize`
6459
--> tests/ui/fn_to_numeric_cast_any.rs:34:13
@@ -68,9 +63,8 @@ LL | let _ = foo as isize;
6863
|
6964
help: did you mean to invoke the function?
7065
|
71-
LL - let _ = foo as isize;
72-
LL + let _ = foo() as isize;
73-
|
66+
LL | let _ = foo() as isize;
67+
| ++
7468

7569
error: casting function pointer `foo` to `u8`
7670
--> tests/ui/fn_to_numeric_cast_any.rs:37:13
@@ -80,9 +74,8 @@ LL | let _ = foo as u8;
8074
|
8175
help: did you mean to invoke the function?
8276
|
83-
LL - let _ = foo as u8;
84-
LL + let _ = foo() as u8;
85-
|
77+
LL | let _ = foo() as u8;
78+
| ++
8679

8780
error: casting function pointer `foo` to `u16`
8881
--> tests/ui/fn_to_numeric_cast_any.rs:39:13
@@ -92,9 +85,8 @@ LL | let _ = foo as u16;
9285
|
9386
help: did you mean to invoke the function?
9487
|
95-
LL - let _ = foo as u16;
96-
LL + let _ = foo() as u16;
97-
|
88+
LL | let _ = foo() as u16;
89+
| ++
9890

9991
error: casting function pointer `foo` to `u32`
10092
--> tests/ui/fn_to_numeric_cast_any.rs:41:13
@@ -104,9 +96,8 @@ LL | let _ = foo as u32;
10496
|
10597
help: did you mean to invoke the function?
10698
|
107-
LL - let _ = foo as u32;
108-
LL + let _ = foo() as u32;
109-
|
99+
LL | let _ = foo() as u32;
100+
| ++
110101

111102
error: casting function pointer `foo` to `u64`
112103
--> tests/ui/fn_to_numeric_cast_any.rs:43:13
@@ -116,9 +107,8 @@ LL | let _ = foo as u64;
116107
|
117108
help: did you mean to invoke the function?
118109
|
119-
LL - let _ = foo as u64;
120-
LL + let _ = foo() as u64;
121-
|
110+
LL | let _ = foo() as u64;
111+
| ++
122112

123113
error: casting function pointer `foo` to `u128`
124114
--> tests/ui/fn_to_numeric_cast_any.rs:45:13
@@ -128,9 +118,8 @@ LL | let _ = foo as u128;
128118
|
129119
help: did you mean to invoke the function?
130120
|
131-
LL - let _ = foo as u128;
132-
LL + let _ = foo() as u128;
133-
|
121+
LL | let _ = foo() as u128;
122+
| ++
134123

135124
error: casting function pointer `foo` to `usize`
136125
--> tests/ui/fn_to_numeric_cast_any.rs:47:13
@@ -140,9 +129,8 @@ LL | let _ = foo as usize;
140129
|
141130
help: did you mean to invoke the function?
142131
|
143-
LL - let _ = foo as usize;
144-
LL + let _ = foo() as usize;
145-
|
132+
LL | let _ = foo() as usize;
133+
| ++
146134

147135
error: casting function pointer `Struct::static_method` to `usize`
148136
--> tests/ui/fn_to_numeric_cast_any.rs:52:13
@@ -152,9 +140,8 @@ LL | let _ = Struct::static_method as usize;
152140
|
153141
help: did you mean to invoke the function?
154142
|
155-
LL - let _ = Struct::static_method as usize;
156-
LL + let _ = Struct::static_method() as usize;
157-
|
143+
LL | let _ = Struct::static_method() as usize;
144+
| ++
158145

159146
error: casting function pointer `f` to `usize`
160147
--> tests/ui/fn_to_numeric_cast_any.rs:57:5
@@ -164,9 +151,8 @@ LL | f as usize
164151
|
165152
help: did you mean to invoke the function?
166153
|
167-
LL - f as usize
168-
LL + f() as usize
169-
|
154+
LL | f() as usize
155+
| ++
170156

171157
error: casting function pointer `T::static_method` to `usize`
172158
--> tests/ui/fn_to_numeric_cast_any.rs:62:5
@@ -176,9 +162,8 @@ LL | T::static_method as usize
176162
|
177163
help: did you mean to invoke the function?
178164
|
179-
LL - T::static_method as usize
180-
LL + T::static_method() as usize
181-
|
165+
LL | T::static_method() as usize
166+
| ++
182167

183168
error: casting function pointer `(clos as fn(u32) -> u32)` to `usize`
184169
--> tests/ui/fn_to_numeric_cast_any.rs:69:13
@@ -188,9 +173,8 @@ LL | let _ = (clos as fn(u32) -> u32) as usize;
188173
|
189174
help: did you mean to invoke the function?
190175
|
191-
LL - let _ = (clos as fn(u32) -> u32) as usize;
192-
LL + let _ = (clos as fn(u32) -> u32)() as usize;
193-
|
176+
LL | let _ = (clos as fn(u32) -> u32)() as usize;
177+
| ++
194178

195179
error: casting function pointer `foo` to `*const ()`
196180
--> tests/ui/fn_to_numeric_cast_any.rs:74:13
@@ -200,9 +184,8 @@ LL | let _ = foo as *const ();
200184
|
201185
help: did you mean to invoke the function?
202186
|
203-
LL - let _ = foo as *const ();
204-
LL + let _ = foo() as *const ();
205-
|
187+
LL | let _ = foo() as *const ();
188+
| ++
206189

207190
error: aborting due to 17 previous errors
208191

src/tools/clippy/tests/ui/implicit_hasher.stderr

+6-9
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,8 @@ LL | pub fn map(map: &mut HashMap<i32, i32>) {}
7878
|
7979
help: add a type parameter for `BuildHasher`
8080
|
81-
LL - pub fn map(map: &mut HashMap<i32, i32>) {}
82-
LL + pub fn map<S: ::std::hash::BuildHasher>(map: &mut HashMap<i32, i32, S>) {}
83-
|
81+
LL | pub fn map<S: ::std::hash::BuildHasher>(map: &mut HashMap<i32, i32, S>) {}
82+
| +++++++++++++++++++++++++++++ +++
8483

8584
error: parameter of type `HashSet` should be generalized over different hashers
8685
--> tests/ui/implicit_hasher.rs:70:22
@@ -90,9 +89,8 @@ LL | pub fn set(set: &mut HashSet<i32>) {}
9089
|
9190
help: add a type parameter for `BuildHasher`
9291
|
93-
LL - pub fn set(set: &mut HashSet<i32>) {}
94-
LL + pub fn set<S: ::std::hash::BuildHasher>(set: &mut HashSet<i32, S>) {}
95-
|
92+
LL | pub fn set<S: ::std::hash::BuildHasher>(set: &mut HashSet<i32, S>) {}
93+
| +++++++++++++++++++++++++++++ +++
9694

9795
error: impl for `HashMap` should be generalized over different hashers
9896
--> tests/ui/implicit_hasher.rs:76:43
@@ -116,9 +114,8 @@ LL | pub async fn election_vote(_data: HashMap<i32, i32>) {}
116114
|
117115
help: add a type parameter for `BuildHasher`
118116
|
119-
LL - pub async fn election_vote(_data: HashMap<i32, i32>) {}
120-
LL + pub async fn election_vote<S: ::std::hash::BuildHasher>(_data: HashMap<i32, i32, S>) {}
121-
|
117+
LL | pub async fn election_vote<S: ::std::hash::BuildHasher>(_data: HashMap<i32, i32, S>) {}
118+
| +++++++++++++++++++++++++++++ +++
122119

123120
error: aborting due to 9 previous errors
124121

0 commit comments

Comments
 (0)