Auto merge of #10970 - y21:read_line_without_trim, r=giraffate
new lint: `read_line_without_trim`
This adds a new lint that checks for calls to `Stdin::read_line` with a reference to a string that is then attempted to parse into an integer type without first trimming it, which is always going to fail at runtime.
This is something that I've seen happen a lot to beginners, because it's easy to run into when following the example of chapter 2 in the book where it shows how to program a guessing game.
It would be nice if we could point beginners to clippy and tell them "let's see what clippy has to say" and have clippy explain to them why it fails 👀
I think this lint can later be "generalized" to work not just for `Stdin` but also any `BufRead` (which seems to be where the guarantee about the trailing newline comes from) and also, matching/comparing it to a string slice that doesn't end in a newline character (e.g. `input == "foo"` is always going to fail)
changelog: new lint: [`read_line_without_trim`]
This commit is contained in:
commit
1e656d8d6d
7 changed files with 255 additions and 0 deletions
36
tests/ui/read_line_without_trim.fixed
Normal file
36
tests/ui/read_line_without_trim.fixed
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
//@run-rustfix
|
||||
|
||||
#![allow(unused)]
|
||||
#![warn(clippy::read_line_without_trim)]
|
||||
|
||||
fn main() {
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
input.pop();
|
||||
let _x: i32 = input.parse().unwrap(); // don't trigger here, newline character is popped
|
||||
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
let _x: i32 = input.trim_end().parse().unwrap();
|
||||
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
let _x = input.trim_end().parse::<i32>().unwrap();
|
||||
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
let _x = input.trim_end().parse::<u32>().unwrap();
|
||||
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
let _x = input.trim_end().parse::<f32>().unwrap();
|
||||
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
let _x = input.trim_end().parse::<bool>().unwrap();
|
||||
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
// this is actually ok, so don't lint here
|
||||
let _x = input.parse::<String>().unwrap();
|
||||
}
|
||||
36
tests/ui/read_line_without_trim.rs
Normal file
36
tests/ui/read_line_without_trim.rs
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
//@run-rustfix
|
||||
|
||||
#![allow(unused)]
|
||||
#![warn(clippy::read_line_without_trim)]
|
||||
|
||||
fn main() {
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
input.pop();
|
||||
let _x: i32 = input.parse().unwrap(); // don't trigger here, newline character is popped
|
||||
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
let _x: i32 = input.parse().unwrap();
|
||||
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
let _x = input.parse::<i32>().unwrap();
|
||||
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
let _x = input.parse::<u32>().unwrap();
|
||||
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
let _x = input.parse::<f32>().unwrap();
|
||||
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
let _x = input.parse::<bool>().unwrap();
|
||||
|
||||
let mut input = String::new();
|
||||
std::io::stdin().read_line(&mut input).unwrap();
|
||||
// this is actually ok, so don't lint here
|
||||
let _x = input.parse::<String>().unwrap();
|
||||
}
|
||||
73
tests/ui/read_line_without_trim.stderr
Normal file
73
tests/ui/read_line_without_trim.stderr
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
error: calling `.parse()` without trimming the trailing newline character
|
||||
--> $DIR/read_line_without_trim.rs:14:25
|
||||
|
|
||||
LL | let _x: i32 = input.parse().unwrap();
|
||||
| ----- ^^^^^^^
|
||||
| |
|
||||
| help: try: `input.trim_end()`
|
||||
|
|
||||
note: call to `.read_line()` here, which leaves a trailing newline character in the buffer, which in turn will cause `.parse()` to fail
|
||||
--> $DIR/read_line_without_trim.rs:13:5
|
||||
|
|
||||
LL | std::io::stdin().read_line(&mut input).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
= note: `-D clippy::read-line-without-trim` implied by `-D warnings`
|
||||
|
||||
error: calling `.parse()` without trimming the trailing newline character
|
||||
--> $DIR/read_line_without_trim.rs:18:20
|
||||
|
|
||||
LL | let _x = input.parse::<i32>().unwrap();
|
||||
| ----- ^^^^^^^^^^^^^^
|
||||
| |
|
||||
| help: try: `input.trim_end()`
|
||||
|
|
||||
note: call to `.read_line()` here, which leaves a trailing newline character in the buffer, which in turn will cause `.parse()` to fail
|
||||
--> $DIR/read_line_without_trim.rs:17:5
|
||||
|
|
||||
LL | std::io::stdin().read_line(&mut input).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: calling `.parse()` without trimming the trailing newline character
|
||||
--> $DIR/read_line_without_trim.rs:22:20
|
||||
|
|
||||
LL | let _x = input.parse::<u32>().unwrap();
|
||||
| ----- ^^^^^^^^^^^^^^
|
||||
| |
|
||||
| help: try: `input.trim_end()`
|
||||
|
|
||||
note: call to `.read_line()` here, which leaves a trailing newline character in the buffer, which in turn will cause `.parse()` to fail
|
||||
--> $DIR/read_line_without_trim.rs:21:5
|
||||
|
|
||||
LL | std::io::stdin().read_line(&mut input).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: calling `.parse()` without trimming the trailing newline character
|
||||
--> $DIR/read_line_without_trim.rs:26:20
|
||||
|
|
||||
LL | let _x = input.parse::<f32>().unwrap();
|
||||
| ----- ^^^^^^^^^^^^^^
|
||||
| |
|
||||
| help: try: `input.trim_end()`
|
||||
|
|
||||
note: call to `.read_line()` here, which leaves a trailing newline character in the buffer, which in turn will cause `.parse()` to fail
|
||||
--> $DIR/read_line_without_trim.rs:25:5
|
||||
|
|
||||
LL | std::io::stdin().read_line(&mut input).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: calling `.parse()` without trimming the trailing newline character
|
||||
--> $DIR/read_line_without_trim.rs:30:20
|
||||
|
|
||||
LL | let _x = input.parse::<bool>().unwrap();
|
||||
| ----- ^^^^^^^^^^^^^^^
|
||||
| |
|
||||
| help: try: `input.trim_end()`
|
||||
|
|
||||
note: call to `.read_line()` here, which leaves a trailing newline character in the buffer, which in turn will cause `.parse()` to fail
|
||||
--> $DIR/read_line_without_trim.rs:29:5
|
||||
|
|
||||
LL | std::io::stdin().read_line(&mut input).unwrap();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 5 previous errors
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue