Skip to content
This repository has been archived by the owner on Nov 24, 2023. It is now read-only.

Commit

Permalink
Merge pull request #185 from ehuss/fix-edge-cases
Browse files Browse the repository at this point in the history
Fix some panics in edge cases.
  • Loading branch information
killercup committed Apr 2, 2020
2 parents 9986e9a + df8401b commit e3b06b0
Show file tree
Hide file tree
Showing 7 changed files with 107 additions and 27 deletions.
12 changes: 9 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,13 @@ fn parse_snippet(span: &DiagnosticSpan) -> Option<Snippet> {
let text_slice = span.text[0].text.chars().collect::<Vec<char>>();

// We subtract `1` because these highlights are 1-based
let start = span.text[0].highlight_start - 1;
let end = span.text[0].highlight_end - 1;
// Check the `min` so that it doesn't attempt to index out-of-bounds when
// the span points to the "end" of the line. For example, a line of
// "foo\n" with a highlight_start of 5 is intended to highlight *after*
// the line. This needs to compensate since the newline has been removed
// from the text slice.
let start = (span.text[0].highlight_start - 1).min(text_slice.len());
let end = (span.text[0].highlight_end - 1).min(text_slice.len());
let lead = text_slice[indent..start].iter().collect();
let mut body: String = text_slice[start..end].iter().collect();

Expand All @@ -122,7 +127,8 @@ fn parse_snippet(span: &DiagnosticSpan) -> Option<Snippet> {

// If we get a DiagnosticSpanLine where highlight_end > text.len(), we prevent an 'out of
// bounds' access by making sure the index is within the array bounds.
let last_tail_index = last.highlight_end.min(last.text.len()) - 1;
// `saturating_sub` is used in case of an empty file
let last_tail_index = last.highlight_end.min(last.text.len()).saturating_sub(1);
let last_slice = last.text.chars().collect::<Vec<char>>();

if span.text.len() > 1 {
Expand Down
42 changes: 42 additions & 0 deletions tests/edge-cases/empty.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"message": "`main` function not found in crate `empty`",
"code": {
"code": "E0601",
"explanation": "No `main` function was found in a binary crate. To fix this error, add a\n`main` function. For example:\n\n```\nfn main() {\n // Your program will start here.\n println!(\"Hello world!\");\n}\n```\n\nIf you don't know the basics of Rust, you can go look to the Rust Book to get\nstarted: https://doc.rust-lang.org/book/\n"
},
"level": "error",
"spans": [
{
"file_name": "empty.rs",
"byte_start": 0,
"byte_end": 0,
"line_start": 0,
"line_end": 0,
"column_start": 1,
"column_end": 1,
"is_primary": true,
"text": [
{
"text": "",
"highlight_start": 1,
"highlight_end": 1
}
],
"label": null,
"suggested_replacement": null,
"suggestion_applicability": null,
"expansion": null
}
],
"children": [
{
"message": "consider adding a `main` function to `empty.rs`",
"code": null,
"level": "note",
"spans": [],
"children": [],
"rendered": null
}
],
"rendered": "error[E0601]: `main` function not found in crate `empty`\n |\n = note: consider adding a `main` function to `empty.rs`\n\n"
}
Empty file added tests/edge-cases/empty.rs
Empty file.
33 changes: 33 additions & 0 deletions tests/edge-cases/no_main.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"message": "`main` function not found in crate `no_main`",
"code": {
"code": "E0601",
"explanation": "No `main` function was found in a binary crate. To fix this error, add a\n`main` function. For example:\n\n```\nfn main() {\n // Your program will start here.\n println!(\"Hello world!\");\n}\n```\n\nIf you don't know the basics of Rust, you can go look to the Rust Book to get\nstarted: https://doc.rust-lang.org/book/\n"
},
"level": "error",
"spans": [
{
"file_name": "no_main.rs",
"byte_start": 26,
"byte_end": 26,
"line_start": 1,
"line_end": 1,
"column_start": 27,
"column_end": 27,
"is_primary": true,
"text": [
{
"text": "// This file has no main.",
"highlight_start": 27,
"highlight_end": 27
}
],
"label": "consider adding a `main` function to `no_main.rs`",
"suggested_replacement": null,
"suggestion_applicability": null,
"expansion": null
}
],
"children": [],
"rendered": "error[E0601]: `main` function not found in crate `no_main`\n --> no_main.rs:1:27\n |\n1 | // This file has no main.\n | ^ consider adding a `main` function to `no_main.rs`\n\n"
}
1 change: 1 addition & 0 deletions tests/edge-cases/no_main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// This file has no main.
41 changes: 18 additions & 23 deletions tests/edge_cases.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,24 @@ use rustfix;
use std::collections::HashSet;
use std::fs;

#[test]
fn multiple_fix_options_yield_no_suggestions() {
let json = fs::read_to_string("./tests/edge-cases/skip-multi-option-lints.json").unwrap();
let expected_suggestions =
rustfix::get_suggestions_from_json(&json, &HashSet::new(), rustfix::Filter::Everything)
macro_rules! expect_empty_json_test {
($name:ident, $file:expr) => {
#[test]
fn $name() {
let json = fs::read_to_string(concat!("./tests/edge-cases/", $file)).unwrap();
let expected_suggestions = rustfix::get_suggestions_from_json(
&json,
&HashSet::new(),
rustfix::Filter::Everything,
)
.unwrap();
assert!(expected_suggestions.is_empty());
assert!(expected_suggestions.is_empty());
}
};
}

#[test]
fn out_of_bounds_test() {
let json = fs::read_to_string("./tests/edge-cases/out_of_bounds.recorded.json").unwrap();
let expected_suggestions =
rustfix::get_suggestions_from_json(&json, &HashSet::new(), rustfix::Filter::Everything)
.unwrap();
assert!(expected_suggestions.is_empty());
}

#[test]
fn utf8_identifiers_test() {
let json = fs::read_to_string("./tests/edge-cases/utf8_idents.recorded.json").unwrap();
let expected_suggestions =
rustfix::get_suggestions_from_json(&json, &HashSet::new(), rustfix::Filter::Everything)
.unwrap();
assert!(expected_suggestions.is_empty());
}
expect_empty_json_test! {multiple_fix_options_yield_no_suggestions, "skip-multi-option-lints.json"}
expect_empty_json_test! {out_of_bounds_test, "out_of_bounds.recorded.json"}
expect_empty_json_test! {utf8_identifiers_test, "utf8_idents.recorded.json"}
expect_empty_json_test! {empty, "empty.json"}
expect_empty_json_test! {no_main, "no_main.json"}
5 changes: 4 additions & 1 deletion tests/parse_and_replace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ fn compile(file: &Path, mode: &str) -> Result<Output, Error> {
fn compile_and_get_json_errors(file: &Path, mode: &str) -> Result<String, Error> {
let res = compile(file, mode)?;
let stderr = String::from_utf8(res.stderr)?;
if stderr.contains("is only accepted on the nightly compiler") {
panic!("rustfix tests require a nightly compiler");
}

match res.status.code() {
Some(0) | Some(1) | Some(101) => Ok(stderr),
Expand Down Expand Up @@ -160,7 +163,7 @@ fn test_rustfix_with_file<P: AsRef<Path>>(file: P, mode: &str) -> Result<(), Err
))?;
let expected_suggestions =
rustfix::get_suggestions_from_json(&expected_json, &HashSet::new(), filter_suggestions)
.context("could not load expected suggesitons")?;
.context("could not load expected suggestions")?;

ensure!(
expected_suggestions == suggestions,
Expand Down

0 comments on commit e3b06b0

Please sign in to comment.