diff --git a/src/parser.rs b/src/parser.rs index 173995cb..714df779 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -226,6 +226,15 @@ pub(crate) enum ErrorMatchKind { Code(Spanned), } +impl ErrorMatchKind { + fn span(&self) -> &Span { + match self { + Self::Pattern { pattern, .. } => &pattern.span, + Self::Code(code) => &code.span, + } + } +} + #[derive(Debug, Clone)] pub(crate) struct ErrorMatch { pub(crate) kind: ErrorMatchKind, @@ -254,6 +263,21 @@ impl Condition { } } +enum ParsePatternResult { + Other, + ErrorAbove { + match_line: NonZeroUsize, + }, + ErrorBelow { + span: Span, + match_line: NonZeroUsize, + }, + Fallthrough { + span: Span, + idx: usize, + }, +} + impl Comments { pub(crate) fn parse_file( comments: Comments, @@ -279,6 +303,7 @@ impl Comments { let defaults = std::mem::take(parser.comments.revisioned.get_mut(&[][..]).unwrap()); + let mut delayed_fallthrough = Vec::new(); let mut fallthrough_to = None; // The line that a `|` will refer to. let mut last_line = 0; for (l, line) in content.as_ref().lines().enumerate() { @@ -291,8 +316,37 @@ impl Comments { col_start: NonZeroUsize::new(1).unwrap(), col_end: NonZeroUsize::new(line.chars().count() + 1).unwrap(), }; - match parser.parse_checked_line(&mut fallthrough_to, Spanned::new(line, span)) { - Ok(()) => {} + match parser.parse_checked_line(fallthrough_to, Spanned::new(line, span)) { + Ok(ParsePatternResult::Other) => { + fallthrough_to = None; + } + Ok(ParsePatternResult::ErrorAbove { match_line }) => { + fallthrough_to = Some(match_line); + } + Ok(ParsePatternResult::Fallthrough { span, idx }) => { + delayed_fallthrough.push((span, l, idx)); + } + Ok(ParsePatternResult::ErrorBelow { span, match_line }) => { + if fallthrough_to.is_some() { + parser.error( + span, + "`//~v` comment immediately following a `//~^` comment chain", + ); + } + + for (span, line, idx) in delayed_fallthrough.drain(..) { + if let Some(rev) = parser + .comments + .revisioned + .values_mut() + .find(|rev| rev.error_matches[idx].kind.span().line_start == line) + { + rev.error_matches[idx].line = match_line; + } else { + parser.error(span, "`//~|` comment not attached to anchoring matcher"); + } + } + } Err(e) => parser.error(e.span, format!("Comment is not utf8: {:?}", e.content)), } } @@ -337,6 +391,10 @@ impl Comments { } } + for (span, ..) in delayed_fallthrough { + parser.error(span, "`//~|` comment not attached to anchoring matcher"); + } + let Revisioned { span, ignore, @@ -397,18 +455,18 @@ impl Comments { impl CommentParser { fn parse_checked_line( &mut self, - fallthrough_to: &mut Option, + fallthrough_to: Option, line: Spanned<&[u8]>, - ) -> std::result::Result<(), Spanned> { + ) -> std::result::Result> { + let mut res = ParsePatternResult::Other; if let Some(command) = line.strip_prefix(b"//@") { self.parse_command(command.to_str()?.trim()) } else if let Some((_, pattern)) = line.split_once_str("//~") { let (revisions, pattern) = self.parse_revisions(pattern.to_str()?); self.revisioned(revisions, |this| { - this.parse_pattern(pattern, fallthrough_to) + res = this.parse_pattern(pattern, fallthrough_to); }) } else { - *fallthrough_to = None; for pos in line.clone().find_iter("//") { let (_, rest) = line.clone().to_str()?.split_at(pos + 2); for rest in std::iter::once(rest.clone()).chain(rest.strip_prefix(" ")) { @@ -448,7 +506,7 @@ impl CommentParser { } } } - Ok(()) + Ok(res) } } @@ -835,15 +893,27 @@ impl CommentParser<&mut Revisioned> { // (\[[a-z]+(,[a-z]+)*\])? // (?P\||[\^]+)? * // ((?PERROR|HELP|WARN|NOTE): (?P.*))|(?P[a-z0-9_:]+) - fn parse_pattern(&mut self, pattern: Spanned<&str>, fallthrough_to: &mut Option) { + fn parse_pattern( + &mut self, + pattern: Spanned<&str>, + fallthrough_to: Option, + ) -> ParsePatternResult { let c = pattern.chars().next(); + let mut res = ParsePatternResult::Other; + let (match_line, pattern) = match c { Some(Spanned { content: '|', span }) => ( match fallthrough_to { - Some(fallthrough) => *fallthrough, + Some(match_line) => { + res = ParsePatternResult::ErrorAbove { match_line }; + match_line + } None => { - self.error(span, "`//~|` pattern without preceding line"); - return; + res = ParsePatternResult::Fallthrough { + span, + idx: self.error_matches.len(), + }; + pattern.span.line_start } }, pattern.split_at(1).1, @@ -862,14 +932,19 @@ impl CommentParser<&mut Revisioned> { { // lines are one-indexed, so a target line of 0 is invalid, but also // prevented via `NonZeroUsize` - Some(match_line) => (match_line, pattern.split_at(offset).1), + Some(match_line) => { + res = ParsePatternResult::ErrorAbove { match_line }; + (match_line, pattern.split_at(offset).1) + } _ => { self.error(pattern.span(), format!( "//~^ pattern is trying to refer to {} lines above, but there are only {} lines above", offset, pattern.line().get() - 1, )); - return; + return ParsePatternResult::ErrorAbove { + match_line: pattern.span().line_start, + }; } } } @@ -885,7 +960,13 @@ impl CommentParser<&mut Revisioned> { .checked_add(offset) .and_then(NonZeroUsize::new) { - Some(match_line) => (match_line, pattern.split_at(offset).1), + Some(match_line) => { + res = ParsePatternResult::ErrorBelow { + span: pattern.span(), + match_line, + }; + (match_line, pattern.split_at(offset).1) + } _ => { // The line count of the file is not yet known so we can only check // if the resulting line is in the range of a usize. @@ -893,14 +974,17 @@ impl CommentParser<&mut Revisioned> { "//~v pattern is trying to refer to {} lines below, which is more than ui_test can count", offset, )); - return; + return ParsePatternResult::ErrorBelow { + span: pattern.span(), + match_line: pattern.span().line_start, + }; } } } Some(_) => (pattern.span().line_start, pattern), None => { self.error(pattern.span(), "no pattern specified"); - return; + return res; } }; @@ -916,7 +1000,7 @@ impl CommentParser<&mut Revisioned> { Ok(level) => level, Err(msg) => { self.error(level.span(), msg); - return; + return res; } }; @@ -933,13 +1017,13 @@ impl CommentParser<&mut Revisioned> { } else if (*level_or_code).parse::().is_ok() { // Shouldn't conflict with any real diagnostic code self.error(level_or_code.span(), "no `:` after level found"); - return; + return res; } else if !pattern.trim_start().is_empty() { self.error( pattern.span(), format!("text found after error code `{}`", *level_or_code), ); - return; + return res; } else { self.error_matches.push(ErrorMatch { kind: ErrorMatchKind::Code(Spanned::new( @@ -950,7 +1034,7 @@ impl CommentParser<&mut Revisioned> { }); }; - *fallthrough_to = Some(match_line); + res } } diff --git a/tests/integrations/basic-fail/Cargo.stdout b/tests/integrations/basic-fail/Cargo.stdout index e096cfba..6e617eab 100644 --- a/tests/integrations/basic-fail/Cargo.stdout +++ b/tests/integrations/basic-fail/Cargo.stdout @@ -11,9 +11,13 @@ tests/actual_tests/exit_code_fail.rs ... FAILED tests/actual_tests/filters.rs ... FAILED tests/actual_tests/foomp.rs ... FAILED tests/actual_tests/foomp2.rs ... FAILED +tests/actual_tests/joined_wrong_order.rs ... FAILED +tests/actual_tests/lone_joined_pattern.rs ... FAILED tests/actual_tests/pattern_too_many_arrow.rs ... FAILED tests/actual_tests/pattern_too_many_arrow_above.rs ... FAILED tests/actual_tests/rustc_ice.rs ... FAILED +tests/actual_tests/touching_above_below.rs ... FAILED +tests/actual_tests/touching_above_below_chain.rs ... FAILED FAILED TEST: tests/actual_tests/bad_pattern.rs command: "rustc" "--error-format=json" "--extern" "basic_fail=$DIR/tests/integrations/basic-fail/../../../target/$TMP/$TRIPLE/debug/libbasic_fail.rlib" "--extern" "basic_fail=$DIR/tests/integrations/basic-fail/../../../target/$TMP/$TRIPLE/debug/libbasic_fail-$HASH.rmeta" "-L" "$DIR/tests/integrations/basic-fail/../../../target/$TMP/$TRIPLE/debug" "-L" "$DIR/tests/integrations/basic-fail/../../../target/$TMP/$TRIPLE/debug" "--out-dir" "$TMP "tests/actual_tests/bad_pattern.rs" "--edition" "2021" @@ -253,6 +257,45 @@ full stdout: +FAILED TEST: tests/actual_tests/joined_wrong_order.rs +command: "parse comments" + +error: `//~|` comment not attached to anchoring matcher + --> tests/actual_tests/joined_wrong_order.rs:5:8 + | +5 | //~| unused_mut + | ^ + | + +full stderr: + +full stdout: + + + +FAILED TEST: tests/actual_tests/lone_joined_pattern.rs +command: "parse comments" + +error: `//~|` comment not attached to anchoring matcher + --> tests/actual_tests/lone_joined_pattern.rs:2:8 + | +2 | //~| ERROR: mismatched types + | ^ + | + +error: `//~|` comment not attached to anchoring matcher + --> tests/actual_tests/lone_joined_pattern.rs:4:8 + | +4 | //~| ERROR: mismatched types + | ^ + | + +full stderr: + +full stdout: + + + FAILED TEST: tests/actual_tests/pattern_too_many_arrow.rs command: "parse comments" @@ -331,6 +374,38 @@ end of query stack full stdout: + +FAILED TEST: tests/actual_tests/touching_above_below.rs +command: "parse comments" + +error: `//~v` comment immediately following a `//~^` comment chain + --> tests/actual_tests/touching_above_below.rs:4:8 + | +4 | //~v ERROR: mismatched types + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + +full stderr: + +full stdout: + + + +FAILED TEST: tests/actual_tests/touching_above_below_chain.rs +command: "parse comments" + +error: `//~v` comment immediately following a `//~^` comment chain + --> tests/actual_tests/touching_above_below_chain.rs:5:8 + | +5 | //~v ERROR: unused variable + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + +full stderr: + +full stdout: + + FAILURES: tests/actual_tests/bad_pattern.rs tests/actual_tests/executable.rs @@ -339,11 +414,15 @@ FAILURES: tests/actual_tests/filters.rs tests/actual_tests/foomp.rs tests/actual_tests/foomp2.rs + tests/actual_tests/joined_wrong_order.rs + tests/actual_tests/lone_joined_pattern.rs tests/actual_tests/pattern_too_many_arrow.rs tests/actual_tests/pattern_too_many_arrow_above.rs tests/actual_tests/rustc_ice.rs + tests/actual_tests/touching_above_below.rs + tests/actual_tests/touching_above_below_chain.rs -test result: FAIL. 10 failed; +test result: FAIL. 14 failed; Building dependencies ... ok tests/actual_tests_bless/aux_build_not_found.rs ... FAILED @@ -931,9 +1010,13 @@ tests/actual_tests/exit_code_fail.rs ... FAILED tests/actual_tests/filters.rs ... FAILED tests/actual_tests/foomp.rs ... FAILED tests/actual_tests/foomp2.rs ... FAILED +tests/actual_tests/joined_wrong_order.rs ... FAILED +tests/actual_tests/lone_joined_pattern.rs ... FAILED tests/actual_tests/pattern_too_many_arrow.rs ... FAILED tests/actual_tests/pattern_too_many_arrow_above.rs ... FAILED tests/actual_tests/rustc_ice.rs ... FAILED +tests/actual_tests/touching_above_below.rs ... FAILED +tests/actual_tests/touching_above_below_chain.rs ... FAILED FAILED TEST: tests/actual_tests/bad_pattern.rs command: "$CMD" "tests/actual_tests/bad_pattern.rs" "--edition" "2021" @@ -1005,6 +1088,45 @@ full stdout: could not spawn `"invalid_foobarlaksdfalsdfj"` as a process +FAILED TEST: tests/actual_tests/joined_wrong_order.rs +command: "parse comments" + +error: `//~|` comment not attached to anchoring matcher + --> tests/actual_tests/joined_wrong_order.rs:5:8 + | +5 | //~| unused_mut + | ^ + | + +full stderr: + +full stdout: + + + +FAILED TEST: tests/actual_tests/lone_joined_pattern.rs +command: "parse comments" + +error: `//~|` comment not attached to anchoring matcher + --> tests/actual_tests/lone_joined_pattern.rs:2:8 + | +2 | //~| ERROR: mismatched types + | ^ + | + +error: `//~|` comment not attached to anchoring matcher + --> tests/actual_tests/lone_joined_pattern.rs:4:8 + | +4 | //~| ERROR: mismatched types + | ^ + | + +full stderr: + +full stdout: + + + FAILED TEST: tests/actual_tests/pattern_too_many_arrow.rs command: "parse comments" @@ -1045,6 +1167,38 @@ No such file or directory full stdout: could not spawn `"invalid_foobarlaksdfalsdfj"` as a process + +FAILED TEST: tests/actual_tests/touching_above_below.rs +command: "parse comments" + +error: `//~v` comment immediately following a `//~^` comment chain + --> tests/actual_tests/touching_above_below.rs:4:8 + | +4 | //~v ERROR: mismatched types + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | + +full stderr: + +full stdout: + + + +FAILED TEST: tests/actual_tests/touching_above_below_chain.rs +command: "parse comments" + +error: `//~v` comment immediately following a `//~^` comment chain + --> tests/actual_tests/touching_above_below_chain.rs:5:8 + | +5 | //~v ERROR: unused variable + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + +full stderr: + +full stdout: + + FAILURES: tests/actual_tests/bad_pattern.rs tests/actual_tests/executable.rs @@ -1053,11 +1207,15 @@ FAILURES: tests/actual_tests/filters.rs tests/actual_tests/foomp.rs tests/actual_tests/foomp2.rs + tests/actual_tests/joined_wrong_order.rs + tests/actual_tests/lone_joined_pattern.rs tests/actual_tests/pattern_too_many_arrow.rs tests/actual_tests/pattern_too_many_arrow_above.rs tests/actual_tests/rustc_ice.rs + tests/actual_tests/touching_above_below.rs + tests/actual_tests/touching_above_below_chain.rs -test result: FAIL. 10 failed; +test result: FAIL. 14 failed; running 0 tests diff --git a/tests/integrations/basic-fail/tests/actual_tests/joined_wrong_order.rs b/tests/integrations/basic-fail/tests/actual_tests/joined_wrong_order.rs new file mode 100644 index 00000000..9afef5dd --- /dev/null +++ b/tests/integrations/basic-fail/tests/actual_tests/joined_wrong_order.rs @@ -0,0 +1,7 @@ +#![deny(unused_mut, unused_variables)] + +fn main() { + //~vv unused_variables + //~| unused_mut + let mut x = 0u32; +} diff --git a/tests/integrations/basic-fail/tests/actual_tests/lone_joined_pattern.rs b/tests/integrations/basic-fail/tests/actual_tests/lone_joined_pattern.rs new file mode 100644 index 00000000..55784cd3 --- /dev/null +++ b/tests/integrations/basic-fail/tests/actual_tests/lone_joined_pattern.rs @@ -0,0 +1,7 @@ +fn main() { + //~| ERROR: mismatched types + use_unit(1_u32); + //~| ERROR: mismatched types +} + +fn use_unit(_: ()) {} diff --git a/tests/integrations/basic-fail/tests/actual_tests/touching_above_below.rs b/tests/integrations/basic-fail/tests/actual_tests/touching_above_below.rs new file mode 100644 index 00000000..8c1208a9 --- /dev/null +++ b/tests/integrations/basic-fail/tests/actual_tests/touching_above_below.rs @@ -0,0 +1,8 @@ +fn main() { + use_unit(1_u32); + //~^ ERROR: mismatched types + //~v ERROR: mismatched types + use_unit(1_u32); +} + +fn use_unit(_: ()) {} diff --git a/tests/integrations/basic-fail/tests/actual_tests/touching_above_below_chain.rs b/tests/integrations/basic-fail/tests/actual_tests/touching_above_below_chain.rs new file mode 100644 index 00000000..477c00d7 --- /dev/null +++ b/tests/integrations/basic-fail/tests/actual_tests/touching_above_below_chain.rs @@ -0,0 +1,9 @@ +fn main() { + use_unit(1_u32); + //~^ ERROR: mismatched types + //~| ERROR: variable does not need to be mutable + //~v ERROR: unused variable + let mut x = 0u32; +} + +fn use_unit(_: ()) {} diff --git a/tests/integrations/basic/Cargo.stdout b/tests/integrations/basic/Cargo.stdout index 9e04efcc..1dff073d 100644 --- a/tests/integrations/basic/Cargo.stdout +++ b/tests/integrations/basic/Cargo.stdout @@ -17,6 +17,9 @@ tests/actual_tests/error_above.rs ... ok tests/actual_tests/executable.rs ... ok tests/actual_tests/foomp-rustfix.rs ... ok tests/actual_tests/foomp.rs ... ok +tests/actual_tests/joined_above.rs ... ok +tests/actual_tests/joined_below.rs ... ok +tests/actual_tests/joined_mixed.rs ... ok tests/actual_tests/match_diagnostic_code.rs ... ok tests/actual_tests/no_rustfix.rs ... ok tests/actual_tests/stdin.rs ... ok @@ -24,7 +27,7 @@ tests/actual_tests/unicode.rs ... ok tests/actual_tests/windows_paths.rs ... ok tests/actual_tests/subdir/aux_proc_macro.rs ... ok -test result: ok. 12 passed; +test result: ok. 15 passed; running 0 tests diff --git a/tests/integrations/basic/tests/actual_tests/joined_above.fixed b/tests/integrations/basic/tests/actual_tests/joined_above.fixed new file mode 100644 index 00000000..1455f3a2 --- /dev/null +++ b/tests/integrations/basic/tests/actual_tests/joined_above.fixed @@ -0,0 +1,7 @@ +#![deny(unused_mut, unused_variables)] + +fn main() { + //~| unused_mut + //~v unused_variables + let _x = 0u32; +} diff --git a/tests/integrations/basic/tests/actual_tests/joined_above.rs b/tests/integrations/basic/tests/actual_tests/joined_above.rs new file mode 100644 index 00000000..173d4287 --- /dev/null +++ b/tests/integrations/basic/tests/actual_tests/joined_above.rs @@ -0,0 +1,7 @@ +#![deny(unused_mut, unused_variables)] + +fn main() { + //~| unused_mut + //~v unused_variables + let mut x = 0u32; +} diff --git a/tests/integrations/basic/tests/actual_tests/joined_above.stderr b/tests/integrations/basic/tests/actual_tests/joined_above.stderr new file mode 100644 index 00000000..00f9f309 --- /dev/null +++ b/tests/integrations/basic/tests/actual_tests/joined_above.stderr @@ -0,0 +1,28 @@ +error: unused variable: `x` + --> tests/actual_tests/joined_above.rs:6:13 + | +6 | let mut x = 0u32; + | ^ help: if this is intentional, prefix it with an underscore: `_x` + | +note: the lint level is defined here + --> tests/actual_tests/joined_above.rs:1:21 + | +1 | #![deny(unused_mut, unused_variables)] + | ^^^^^^^^^^^^^^^^ + +error: variable does not need to be mutable + --> tests/actual_tests/joined_above.rs:6:9 + | +6 | let mut x = 0u32; + | ----^ + | | + | help: remove this `mut` + | +note: the lint level is defined here + --> tests/actual_tests/joined_above.rs:1:9 + | +1 | #![deny(unused_mut, unused_variables)] + | ^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/integrations/basic/tests/actual_tests/joined_below.fixed b/tests/integrations/basic/tests/actual_tests/joined_below.fixed new file mode 100644 index 00000000..d8ed3d2b --- /dev/null +++ b/tests/integrations/basic/tests/actual_tests/joined_below.fixed @@ -0,0 +1,7 @@ +#![deny(unused_mut, unused_variables)] + +fn main() { + let _x = 0u32; + //~^ unused_variables + //~| unused_mut +} diff --git a/tests/integrations/basic/tests/actual_tests/joined_below.rs b/tests/integrations/basic/tests/actual_tests/joined_below.rs new file mode 100644 index 00000000..ca53bd4b --- /dev/null +++ b/tests/integrations/basic/tests/actual_tests/joined_below.rs @@ -0,0 +1,7 @@ +#![deny(unused_mut, unused_variables)] + +fn main() { + let mut x = 0u32; + //~^ unused_variables + //~| unused_mut +} diff --git a/tests/integrations/basic/tests/actual_tests/joined_below.stderr b/tests/integrations/basic/tests/actual_tests/joined_below.stderr new file mode 100644 index 00000000..f66d7359 --- /dev/null +++ b/tests/integrations/basic/tests/actual_tests/joined_below.stderr @@ -0,0 +1,28 @@ +error: unused variable: `x` + --> tests/actual_tests/joined_below.rs:4:13 + | +4 | let mut x = 0u32; + | ^ help: if this is intentional, prefix it with an underscore: `_x` + | +note: the lint level is defined here + --> tests/actual_tests/joined_below.rs:1:21 + | +1 | #![deny(unused_mut, unused_variables)] + | ^^^^^^^^^^^^^^^^ + +error: variable does not need to be mutable + --> tests/actual_tests/joined_below.rs:4:9 + | +4 | let mut x = 0u32; + | ----^ + | | + | help: remove this `mut` + | +note: the lint level is defined here + --> tests/actual_tests/joined_below.rs:1:9 + | +1 | #![deny(unused_mut, unused_variables)] + | ^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/integrations/basic/tests/actual_tests/joined_mixed.fixed b/tests/integrations/basic/tests/actual_tests/joined_mixed.fixed new file mode 100644 index 00000000..e0aa8d69 --- /dev/null +++ b/tests/integrations/basic/tests/actual_tests/joined_mixed.fixed @@ -0,0 +1,11 @@ +#![deny(unused_mut, unused_variables)] + +fn main() { + let _x = 0u32; + //~^ unused_variables + //~| unused_mut + + //~| unused_mut + //~v unused_variables + let _y = 0u32; +} diff --git a/tests/integrations/basic/tests/actual_tests/joined_mixed.rs b/tests/integrations/basic/tests/actual_tests/joined_mixed.rs new file mode 100644 index 00000000..75ec9bde --- /dev/null +++ b/tests/integrations/basic/tests/actual_tests/joined_mixed.rs @@ -0,0 +1,11 @@ +#![deny(unused_mut, unused_variables)] + +fn main() { + let mut x = 0u32; + //~^ unused_variables + //~| unused_mut + + //~| unused_mut + //~v unused_variables + let mut y = 0u32; +} diff --git a/tests/integrations/basic/tests/actual_tests/joined_mixed.stderr b/tests/integrations/basic/tests/actual_tests/joined_mixed.stderr new file mode 100644 index 00000000..5cd76065 --- /dev/null +++ b/tests/integrations/basic/tests/actual_tests/joined_mixed.stderr @@ -0,0 +1,42 @@ +error: unused variable: `x` + --> tests/actual_tests/joined_mixed.rs:4:13 + | +4 | let mut x = 0u32; + | ^ help: if this is intentional, prefix it with an underscore: `_x` + | +note: the lint level is defined here + --> tests/actual_tests/joined_mixed.rs:1:21 + | +1 | #![deny(unused_mut, unused_variables)] + | ^^^^^^^^^^^^^^^^ + +error: unused variable: `y` + --> tests/actual_tests/joined_mixed.rs:10:13 + | +10 | let mut y = 0u32; + | ^ help: if this is intentional, prefix it with an underscore: `_y` + +error: variable does not need to be mutable + --> tests/actual_tests/joined_mixed.rs:4:9 + | +4 | let mut x = 0u32; + | ----^ + | | + | help: remove this `mut` + | +note: the lint level is defined here + --> tests/actual_tests/joined_mixed.rs:1:9 + | +1 | #![deny(unused_mut, unused_variables)] + | ^^^^^^^^^^ + +error: variable does not need to be mutable + --> tests/actual_tests/joined_mixed.rs:10:9 + | +10 | let mut y = 0u32; + | ----^ + | | + | help: remove this `mut` + +error: aborting due to 4 previous errors +