Skip to content

Commit b7c48b4

Browse files
authored
Rollup merge of #91607 - FabianWolff:issue-91560-const-span, r=jackh726
Make `span_extend_to_prev_str()` more robust Fixes #91560. The logic in `span_extend_to_prev_str()` is currently quite brittle and fails if there is extra whitespace or something else in between, and it also should return an `Option` but doesn't currently.
2 parents ce4df92 + 71ff16b commit b7c48b4

File tree

7 files changed

+123
-46
lines changed

7 files changed

+123
-46
lines changed

compiler/rustc_resolve/src/diagnostics.rs

+19-19
Original file line numberDiff line numberDiff line change
@@ -453,28 +453,28 @@ impl<'a> Resolver<'a> {
453453
// edit:
454454
// only do this if the const and usage of the non-constant value are on the same line
455455
// the further the two are apart, the higher the chance of the suggestion being wrong
456-
// also make sure that the pos for the suggestion is not 0 (ICE #90878)
457456

458-
let sp =
459-
self.session.source_map().span_extend_to_prev_str(ident.span, current, true);
460-
461-
let pos_for_suggestion = sp.lo().0.saturating_sub(current.len() as u32);
457+
let sp = self
458+
.session
459+
.source_map()
460+
.span_extend_to_prev_str(ident.span, current, true, false);
462461

463-
if sp.lo().0 == 0
464-
|| pos_for_suggestion == 0
465-
|| self.session.source_map().is_multiline(sp)
466-
{
467-
err.span_label(ident.span, &format!("this would need to be a `{}`", sugg));
468-
} else {
469-
let sp = sp.with_lo(BytePos(pos_for_suggestion));
470-
err.span_suggestion(
471-
sp,
472-
&format!("consider using `{}` instead of `{}`", sugg, current),
473-
format!("{} {}", sugg, ident),
474-
Applicability::MaybeIncorrect,
475-
);
476-
err.span_label(span, "non-constant value");
462+
match sp {
463+
Some(sp) if !self.session.source_map().is_multiline(sp) => {
464+
let sp = sp.with_lo(BytePos(sp.lo().0 - (current.len() as u32)));
465+
err.span_suggestion(
466+
sp,
467+
&format!("consider using `{}` instead of `{}`", sugg, current),
468+
format!("{} {}", sugg, ident),
469+
Applicability::MaybeIncorrect,
470+
);
471+
err.span_label(span, "non-constant value");
472+
}
473+
_ => {
474+
err.span_label(ident.span, &format!("this would need to be a `{}`", sugg));
475+
}
477476
}
477+
478478
err
479479
}
480480
ResolutionError::BindingShadowsSomethingUnacceptable {

compiler/rustc_span/src/source_map.rs

+27-13
Original file line numberDiff line numberDiff line change
@@ -629,26 +629,41 @@ impl SourceMap {
629629
}
630630

631631
/// Extends the given `Span` to just after the previous occurrence of `pat` when surrounded by
632-
/// whitespace. Returns the same span if no character could be found or if an error occurred
633-
/// while retrieving the code snippet.
634-
pub fn span_extend_to_prev_str(&self, sp: Span, pat: &str, accept_newlines: bool) -> Span {
632+
/// whitespace. Returns None if the pattern could not be found or if an error occurred while
633+
/// retrieving the code snippet.
634+
pub fn span_extend_to_prev_str(
635+
&self,
636+
sp: Span,
637+
pat: &str,
638+
accept_newlines: bool,
639+
include_whitespace: bool,
640+
) -> Option<Span> {
635641
// assure that the pattern is delimited, to avoid the following
636642
// fn my_fn()
637643
// ^^^^ returned span without the check
638644
// ---------- correct span
645+
let prev_source = self.span_to_prev_source(sp).ok()?;
639646
for ws in &[" ", "\t", "\n"] {
640647
let pat = pat.to_owned() + ws;
641-
if let Ok(prev_source) = self.span_to_prev_source(sp) {
642-
let prev_source = prev_source.rsplit(&pat).next().unwrap_or("").trim_start();
643-
if prev_source.is_empty() && sp.lo().0 != 0 {
644-
return sp.with_lo(BytePos(sp.lo().0 - 1));
645-
} else if accept_newlines || !prev_source.contains('\n') {
646-
return sp.with_lo(BytePos(sp.lo().0 - prev_source.len() as u32));
648+
if let Some(pat_pos) = prev_source.rfind(&pat) {
649+
let just_after_pat_pos = pat_pos + pat.len() - 1;
650+
let just_after_pat_plus_ws = if include_whitespace {
651+
just_after_pat_pos
652+
+ prev_source[just_after_pat_pos..]
653+
.find(|c: char| !c.is_whitespace())
654+
.unwrap_or(0)
655+
} else {
656+
just_after_pat_pos
657+
};
658+
let len = prev_source.len() - just_after_pat_plus_ws;
659+
let prev_source = &prev_source[just_after_pat_plus_ws..];
660+
if accept_newlines || !prev_source.trim_start().contains('\n') {
661+
return Some(sp.with_lo(BytePos(sp.lo().0 - len as u32)));
647662
}
648663
}
649664
}
650665

651-
sp
666+
None
652667
}
653668

654669
/// Returns the source snippet as `String` after the given `Span`.
@@ -927,7 +942,7 @@ impl SourceMap {
927942
}
928943

929944
pub fn generate_fn_name_span(&self, span: Span) -> Option<Span> {
930-
let prev_span = self.span_extend_to_prev_str(span, "fn", true);
945+
let prev_span = self.span_extend_to_prev_str(span, "fn", true, true).unwrap_or(span);
931946
if let Ok(snippet) = self.span_to_snippet(prev_span) {
932947
debug!(
933948
"generate_fn_name_span: span={:?}, prev_span={:?}, snippet={:?}",
@@ -968,8 +983,7 @@ impl SourceMap {
968983
pub fn generate_local_type_param_snippet(&self, span: Span) -> Option<(Span, String)> {
969984
// Try to extend the span to the previous "fn" keyword to retrieve the function
970985
// signature.
971-
let sugg_span = self.span_extend_to_prev_str(span, "fn", false);
972-
if sugg_span != span {
986+
if let Some(sugg_span) = self.span_extend_to_prev_str(span, "fn", false, true) {
973987
if let Ok(snippet) = self.span_to_snippet(sugg_span) {
974988
// Consume the function name.
975989
let mut offset = snippet

src/test/ui/asm/aarch64/parse-error.stderr

+7-7
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ error[E0435]: attempt to use a non-constant value in a constant
382382
--> $DIR/parse-error.rs:39:37
383383
|
384384
LL | let mut foo = 0;
385-
| ---------- help: consider using `const` instead of `let`: `const foo`
385+
| ----------- help: consider using `const` instead of `let`: `const foo`
386386
...
387387
LL | asm!("{}", options(), const foo);
388388
| ^^^ non-constant value
@@ -391,7 +391,7 @@ error[E0435]: attempt to use a non-constant value in a constant
391391
--> $DIR/parse-error.rs:48:44
392392
|
393393
LL | let mut foo = 0;
394-
| ---------- help: consider using `const` instead of `let`: `const foo`
394+
| ----------- help: consider using `const` instead of `let`: `const foo`
395395
...
396396
LL | asm!("{}", clobber_abi("C"), const foo);
397397
| ^^^ non-constant value
@@ -400,7 +400,7 @@ error[E0435]: attempt to use a non-constant value in a constant
400400
--> $DIR/parse-error.rs:55:31
401401
|
402402
LL | let mut foo = 0;
403-
| ---------- help: consider using `const` instead of `let`: `const foo`
403+
| ----------- help: consider using `const` instead of `let`: `const foo`
404404
...
405405
LL | asm!("{a}", a = const foo, a = const bar);
406406
| ^^^ non-constant value
@@ -409,7 +409,7 @@ error[E0435]: attempt to use a non-constant value in a constant
409409
--> $DIR/parse-error.rs:55:46
410410
|
411411
LL | let mut bar = 0;
412-
| ---------- help: consider using `const` instead of `let`: `const bar`
412+
| ----------- help: consider using `const` instead of `let`: `const bar`
413413
...
414414
LL | asm!("{a}", a = const foo, a = const bar);
415415
| ^^^ non-constant value
@@ -418,7 +418,7 @@ error[E0435]: attempt to use a non-constant value in a constant
418418
--> $DIR/parse-error.rs:62:45
419419
|
420420
LL | let mut bar = 0;
421-
| ---------- help: consider using `const` instead of `let`: `const bar`
421+
| ----------- help: consider using `const` instead of `let`: `const bar`
422422
...
423423
LL | asm!("{a}", in("x0") foo, a = const bar);
424424
| ^^^ non-constant value
@@ -427,7 +427,7 @@ error[E0435]: attempt to use a non-constant value in a constant
427427
--> $DIR/parse-error.rs:65:45
428428
|
429429
LL | let mut bar = 0;
430-
| ---------- help: consider using `const` instead of `let`: `const bar`
430+
| ----------- help: consider using `const` instead of `let`: `const bar`
431431
...
432432
LL | asm!("{a}", in("x0") foo, a = const bar);
433433
| ^^^ non-constant value
@@ -436,7 +436,7 @@ error[E0435]: attempt to use a non-constant value in a constant
436436
--> $DIR/parse-error.rs:68:41
437437
|
438438
LL | let mut bar = 0;
439-
| ---------- help: consider using `const` instead of `let`: `const bar`
439+
| ----------- help: consider using `const` instead of `let`: `const bar`
440440
...
441441
LL | asm!("{1}", in("x0") foo, const bar);
442442
| ^^^ non-constant value

src/test/ui/asm/x86_64/parse-error.stderr

+7-7
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ error[E0435]: attempt to use a non-constant value in a constant
394394
--> $DIR/parse-error.rs:39:37
395395
|
396396
LL | let mut foo = 0;
397-
| ---------- help: consider using `const` instead of `let`: `const foo`
397+
| ----------- help: consider using `const` instead of `let`: `const foo`
398398
...
399399
LL | asm!("{}", options(), const foo);
400400
| ^^^ non-constant value
@@ -403,7 +403,7 @@ error[E0435]: attempt to use a non-constant value in a constant
403403
--> $DIR/parse-error.rs:50:44
404404
|
405405
LL | let mut foo = 0;
406-
| ---------- help: consider using `const` instead of `let`: `const foo`
406+
| ----------- help: consider using `const` instead of `let`: `const foo`
407407
...
408408
LL | asm!("{}", clobber_abi("C"), const foo);
409409
| ^^^ non-constant value
@@ -412,7 +412,7 @@ error[E0435]: attempt to use a non-constant value in a constant
412412
--> $DIR/parse-error.rs:57:31
413413
|
414414
LL | let mut foo = 0;
415-
| ---------- help: consider using `const` instead of `let`: `const foo`
415+
| ----------- help: consider using `const` instead of `let`: `const foo`
416416
...
417417
LL | asm!("{a}", a = const foo, a = const bar);
418418
| ^^^ non-constant value
@@ -421,7 +421,7 @@ error[E0435]: attempt to use a non-constant value in a constant
421421
--> $DIR/parse-error.rs:57:46
422422
|
423423
LL | let mut bar = 0;
424-
| ---------- help: consider using `const` instead of `let`: `const bar`
424+
| ----------- help: consider using `const` instead of `let`: `const bar`
425425
...
426426
LL | asm!("{a}", a = const foo, a = const bar);
427427
| ^^^ non-constant value
@@ -430,7 +430,7 @@ error[E0435]: attempt to use a non-constant value in a constant
430430
--> $DIR/parse-error.rs:64:46
431431
|
432432
LL | let mut bar = 0;
433-
| ---------- help: consider using `const` instead of `let`: `const bar`
433+
| ----------- help: consider using `const` instead of `let`: `const bar`
434434
...
435435
LL | asm!("{a}", in("eax") foo, a = const bar);
436436
| ^^^ non-constant value
@@ -439,7 +439,7 @@ error[E0435]: attempt to use a non-constant value in a constant
439439
--> $DIR/parse-error.rs:67:46
440440
|
441441
LL | let mut bar = 0;
442-
| ---------- help: consider using `const` instead of `let`: `const bar`
442+
| ----------- help: consider using `const` instead of `let`: `const bar`
443443
...
444444
LL | asm!("{a}", in("eax") foo, a = const bar);
445445
| ^^^ non-constant value
@@ -448,7 +448,7 @@ error[E0435]: attempt to use a non-constant value in a constant
448448
--> $DIR/parse-error.rs:70:42
449449
|
450450
LL | let mut bar = 0;
451-
| ---------- help: consider using `const` instead of `let`: `const bar`
451+
| ----------- help: consider using `const` instead of `let`: `const bar`
452452
...
453453
LL | asm!("{1}", in("eax") foo, const bar);
454454
| ^^^ non-constant value

src/test/ui/consts/issue-91560.fixed

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Regression test for issue #91560.
2+
3+
// run-rustfix
4+
5+
#![allow(unused,non_upper_case_globals)]
6+
7+
fn foo() {
8+
const length: usize = 2;
9+
//~^ HELP: consider using `const`
10+
let arr = [0; length];
11+
//~^ ERROR: attempt to use a non-constant value in a constant [E0435]
12+
}
13+
14+
fn bar() {
15+
const length: usize = 2;
16+
//~^ HELP: consider using `const`
17+
let arr = [0; length];
18+
//~^ ERROR: attempt to use a non-constant value in a constant [E0435]
19+
}
20+
21+
fn main() {}

src/test/ui/consts/issue-91560.rs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Regression test for issue #91560.
2+
3+
// run-rustfix
4+
5+
#![allow(unused,non_upper_case_globals)]
6+
7+
fn foo() {
8+
let mut length: usize = 2;
9+
//~^ HELP: consider using `const`
10+
let arr = [0; length];
11+
//~^ ERROR: attempt to use a non-constant value in a constant [E0435]
12+
}
13+
14+
fn bar() {
15+
let length: usize = 2;
16+
//~^ HELP: consider using `const`
17+
let arr = [0; length];
18+
//~^ ERROR: attempt to use a non-constant value in a constant [E0435]
19+
}
20+
21+
fn main() {}

src/test/ui/consts/issue-91560.stderr

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0435]: attempt to use a non-constant value in a constant
2+
--> $DIR/issue-91560.rs:10:19
3+
|
4+
LL | let mut length: usize = 2;
5+
| -------------- help: consider using `const` instead of `let`: `const length`
6+
LL |
7+
LL | let arr = [0; length];
8+
| ^^^^^^ non-constant value
9+
10+
error[E0435]: attempt to use a non-constant value in a constant
11+
--> $DIR/issue-91560.rs:17:19
12+
|
13+
LL | let length: usize = 2;
14+
| ------------ help: consider using `const` instead of `let`: `const length`
15+
LL |
16+
LL | let arr = [0; length];
17+
| ^^^^^^ non-constant value
18+
19+
error: aborting due to 2 previous errors
20+
21+
For more information about this error, try `rustc --explain E0435`.

0 commit comments

Comments
 (0)