Skip to content
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

Fix overflow in error emitter #111745

Merged
merged 1 commit into from
May 21, 2023
Merged

Conversation

Badel2
Copy link
Contributor

@Badel2 Badel2 commented May 18, 2023

Fix #109854
Close #94171 (was already fixed before but missing test)

This bug happens when a multipart suggestion spans more than one line.

The fix is to update the acc variable, which didn't handle the case when the text to remove spans multiple lines but the text to add spans only one line.

Also, use usize::try_from instead of as usize to detect overflows earlier in the future, and point to the source of the overflow (the original issue points to a different place where this value is used, not where the overflow had happened).

And finally add an if start != end check to avoid doing any extra work in case of empty ranges.

Long explanation:

Given this test case:

fn generate_setter() {
    String::with_capacity(
    //~^ ERROR this function takes 1 argument but 3 arguments were supplied
    generate_setter,
    r#"
pub(crate) struct Person<T: Clone> {}
"#,
     r#""#,
    );
}

The compiler will try to convert that code into the following:

fn generate_setter() {
    String::with_capacity(
    //~^ ERROR this function takes 1 argument but 3 arguments were supplied
    /* usize */,
    );
}

So it creates a suggestion with 3 separate parts:

// Replace "generate_setter" with "/* usize */"
SubstitutionPart { span: fuzz_input.rs:4:5: 4:20 (#0), snippet: "/* usize */" }
// Remove second arg (multiline string)
SubstitutionPart { span: fuzz_input.rs:4:20: 7:3 (#0), snippet: "" }
// Remove third arg (r#""#)
SubstitutionPart { span: fuzz_input.rs:7:3: 8:11 (#0), snippet: "" }

Each of this parts gets a separate SubstitutionHighlight (this marks the relevant text green in a terminal, the values are 0-indexed so start: 4 means column 5):

SubstitutionHighlight { start: 4, end: 15 }
SubstitutionHighlight { start: 15, end: 15 }
SubstitutionHighlight { start: 18446744073709551614, end: 18446744073709551614 }

The 2nd and 3rd suggestion are empty (start = end) because they only remove text, so there are no additions to highlight. But the 3rd span has overflowed because the compiler assumes that the 3rd suggestion is on the same line as the first suggestion. The 2nd span starts at column 20 and the highlight starts at column 16 (15+1), so that suggestion is good. But since the 3rd span starts at column 3, the result is 3 - 4, or column -1, which turns into -2 with 0-indexed, and that's equivalent to 18446744073709551614 as isize.

With this fix, the resulting SubstitutionHighlight are:

SubstitutionHighlight { start: 4, end: 15 }
SubstitutionHighlight { start: 15, end: 15 }
SubstitutionHighlight { start: 15, end: 15 }

As expected. I guess ideally we shouldn't emit empty highlights when removing text, but I am too scared to change that.

@rustbot
Copy link
Collaborator

rustbot commented May 18, 2023

r? @compiler-errors

(rustbot has picked a reviewer for you, use r? to override)

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels May 18, 2023
// snippet being suggested, so that the *later* suggestions are correctly
// aligned on the screen. Note that cur_hi and cur_lo can be on different
// lines, so cur_hi.col can be smaller than cur_lo.col
acc += len - (cur_hi.col.0 as isize - cur_lo.col.0 as isize);
Copy link
Member

@compiler-errors compiler-errors May 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be simplified to len + cur_lo - cur_hi? maybe that's conceptually not simpler, idk.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, this could be acc += len - col_diff, but it doesn't seem much cleaner to introduce a new variable. So I'll leave it like it is.

@Badel2
Copy link
Contributor Author

Badel2 commented May 19, 2023

I already found the first bug thanks to the usize::try_from, but I will revert that change because it may introduce too much noise. That's because the integer overflow I am trying to solve can only be seen with debug assertions, and usize::try_from().unwrap() will trigger even in normal builds.

This test case:

fn main() {
    let a = 0.le('r',3.+0,3.+0,                      3);
}

Has an overflow because the spans overlap. The error message is mostly correct:

error[E0061]: this method takes 1 argument but 4 arguments were supplied
 --> fuzz_input.rs:2:15
  |
2 |     let a = 0.le('r',3.+0,3.+0,                      3);
  |               ^^ ---      ----                       - unexpected argument of type `{integer}`
  |                  |        |
  |                  |        unexpected argument
  |                  unexpected argument of type `char`
  |
2

help: remove the extra arguments
  |
2 -     let a = 0.le('r',3.+0,3.+0,                      3);
2 +     let a = 0.le(,3.+0);
  |

But these are the spans:

SubstitutionPart { span: fuzz_input.rs:2:18: 2:21 (#0), snippet: "" }
SubstitutionPart { span: fuzz_input.rs:2:26: 2:54 (#0), snippet: "" }
SubstitutionPart { span: fuzz_input.rs:2:31: 2:55 (#0), snippet: "" }

SubstitutionHighlight { start: 17, end: 17 }
SubstitutionHighlight { start: 22, end: 22 }
SubstitutionHighlight { start: 18446744073709551615, end: 18446744073709551615 }

Note that this bug is already present in master, it is not caused by the changes in this PR. The spans overlap because they get expanded in the "delete next comma" logic from here:

span = span.until(next);

Here are some logs that show how the spans are modified inside that function:

span: fuzz_input.rs:2:18: 2:21 (#0)
Include previous comma: expanded fuzz_input.rs:2:54: 2:55 (#0) to fuzz_input.rs:2:31: 2:55 (#0)
span: fuzz_input.rs:2:31: 2:55 (#0)
Include previous comma: expanded fuzz_input.rs:2:27: 2:31 (#0) to fuzz_input.rs:2:26: 2:31 (#0)
Delete next comma: expanded fuzz_input.rs:2:26: 2:31 (#0) to fuzz_input.rs:2:26: 2:54 (#0)
span: fuzz_input.rs:2:26: 2:54 (#0)

I tried to fix it by removing the "delete next comma" logic, and that fixes the overflow, but it breaks other tests.

Copy link
Member

@compiler-errors compiler-errors left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please squash this into one commit. No use in keeping the revert in the history.

@compiler-errors
Copy link
Member

Thanks

@bors r+

@bors
Copy link
Contributor

bors commented May 19, 2023

📌 Commit cbb4100 has been approved by compiler-errors

It is now in the queue for this repository.

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels May 19, 2023
matthiaskrgr added a commit to matthiaskrgr/rust that referenced this pull request May 21, 2023
…piler-errors

Fix overflow in error emitter

Fix rust-lang#109854
Close rust-lang#94171 (was already fixed before but missing test)

This bug happens when a multipart suggestion spans more than one line.

The fix is to update the `acc` variable, which didn't handle the case when the text to remove spans multiple lines but the text to add spans only one line.

Also, use `usize::try_from` instead of  `as usize` to detect overflows earlier in the future, and point to the source of the overflow (the original issue points to a different place where this value is used, not where the overflow had happened).

And finally add an `if start != end` check to avoid doing any extra work in case of empty ranges.

Long explanation:

Given this test case:

```rust
fn generate_setter() {
    String::with_capacity(
    //~^ ERROR this function takes 1 argument but 3 arguments were supplied
    generate_setter,
    r#"
pub(crate) struct Person<T: Clone> {}
"#,
     r#""#,
    );
}
```

The compiler will try to convert that code into the following:

```rust
fn generate_setter() {
    String::with_capacity(
    //~^ ERROR this function takes 1 argument but 3 arguments were supplied
    /* usize */,
    );
}
```

So it creates a suggestion with 3 separate parts:

```
// Replace "generate_setter" with "/* usize */"
SubstitutionPart { span: fuzz_input.rs:4:5: 4:20 (#0), snippet: "/* usize */" }
// Remove second arg (multiline string)
SubstitutionPart { span: fuzz_input.rs:4:20: 7:3 (#0), snippet: "" }
// Remove third arg (r#""#)
SubstitutionPart { span: fuzz_input.rs:7:3: 8:11 (#0), snippet: "" }
```

Each of this parts gets a separate `SubstitutionHighlight` (this marks the relevant text green in a terminal, the values are 0-indexed so `start: 4` means column 5):

```
SubstitutionHighlight { start: 4, end: 15 }
SubstitutionHighlight { start: 15, end: 15 }
SubstitutionHighlight { start: 18446744073709551614, end: 18446744073709551614 }
```

The 2nd and 3rd suggestion are empty (start = end) because they only remove text, so there are no additions to highlight. But the 3rd span has overflowed because the compiler assumes that the 3rd suggestion is on the same line as the first suggestion. The 2nd span starts at column 20 and the highlight starts at column 16 (15+1), so that suggestion is good. But since the 3rd span starts at column 3, the result is `3 - 4`, or column -1, which turns into -2 with 0-indexed, and that's equivalent to `18446744073709551614 as isize`.

With this fix, the resulting `SubstitutionHighlight` are:

```
SubstitutionHighlight { start: 4, end: 15 }
SubstitutionHighlight { start: 15, end: 15 }
SubstitutionHighlight { start: 15, end: 15 }
```

As expected. I guess ideally we shouldn't emit empty highlights when removing text, but I am too scared to change that.
bors added a commit to rust-lang-ci/rust that referenced this pull request May 21, 2023
…iaskrgr

Rollup of 5 pull requests

Successful merges:

 - rust-lang#111745 (Fix overflow in error emitter)
 - rust-lang#111770 (Read beta version from the version file if building from a source tarball)
 - rust-lang#111797 (Migrate GUI colors test to original CSS color format)
 - rust-lang#111809 (Unset MIRI_BLESS for mir-opt-level 4 miri tests)
 - rust-lang#111817 (Migrate GUI colors test to original CSS color format)

r? `@ghost`
`@rustbot` modify labels: rollup
@bors bors merged commit cb5dd1d into rust-lang:master May 21, 2023
@rustbot rustbot added this to the 1.71.0 milestone May 21, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
4 participants