Skip to content

Conversation

samueltardieu
Copy link
Member

This lint detects a problem that happened recently in https://github.com/uutils/coreutils and is described in rust-lang/rust#64144.

changelog: [lines_filter_map_ok]: new lint

@rustbot
Copy link
Collaborator

rustbot commented Mar 23, 2023

r? @flip1995

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

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Mar 23, 2023
@samueltardieu samueltardieu force-pushed the lines-filter-map-ok branch 2 times, most recently from eb59e32 to 1f4c5a2 Compare March 23, 2023 01:04
@tertsdiepraam
Copy link

tertsdiepraam commented Mar 23, 2023

Wow this is awesome! Thanks!

I'm new to clippy lints but if I understand correctly, this checks that there is a BufRead on which the lines() and filter_map(Result::ok) methods are called right?

There is a variation of this that I've seen with .filter_map(|l| l.ok()). That is not covered by this implementation, is it? It might not be super important as other lints might already suggest rewriting the closure into Result::ok, which will in turn trigger this lint.

The same pattern also happens with Stdin, which has a separate lines method and does not implement BufRead. It does however return the same Lines struct. I'm not sure it's really an issue with Stdin though as I haven't gotten it to get into an infinite loop yet, but I think it's possible.

@samueltardieu
Copy link
Member Author

I'm new to clippy lints but if I understand correctly, this checks that there is a BufRead on which the lines() and filter_map(Result::ok) methods are called right?

Yes.

There is a variation of this that I've seen with .filter_map(|l| l.ok()). That is not covered by this implementation, is it? It might not be super important as other lints might already suggest rewriting the closure into Result::ok, which will in turn trigger this lint.

I'll have a look, as there is a lint for possible η-reduction but it is activated only in pedantic mode.

The same pattern also happens with Stdin, which has a separate lines method and does not implement BufRead. It does however return the same Lines struct. I'm not sure it's really an issue with Stdin though as I haven't gotten it to get into an infinite loop yet, but I think it's possible.

Indeed. stdin().lock().lines() is covered, as the lines() method comes from BufRead, but stdin.lines() is a special case which could be easily integrated by checking for the Lines type. I'll put this PR in draft mode until I implement it.

@samueltardieu samueltardieu marked this pull request as draft March 23, 2023 12:14
@bors
Copy link
Contributor

bors commented Mar 24, 2023

☔ The latest upstream changes (presumably #10489) made this pull request unmergeable. Please resolve the merge conflicts.

@samueltardieu samueltardieu force-pushed the lines-filter-map-ok branch 2 times, most recently from 32ff41c to 1023cf0 Compare March 24, 2023 17:37
@samueltardieu samueltardieu marked this pull request as ready for review March 24, 2023 17:37
@samueltardieu
Copy link
Member Author

The proposed lint now triggers with anything returning std::io::Lines (including std::io::stdin().lines()), and detects |x| x.ok() in addition to Result::ok.

A lintcheck with the 500 top most downloaded recent crates shows no hit.

@samueltardieu samueltardieu force-pushed the lines-filter-map-ok branch 2 times, most recently from d04dab1 to 51b6693 Compare March 24, 2023 17:41
@bors
Copy link
Contributor

bors commented Mar 30, 2023

☔ The latest upstream changes (presumably #9102) made this pull request unmergeable. Please resolve the merge conflicts.

@samueltardieu samueltardieu force-pushed the lines-filter-map-ok branch 3 times, most recently from a2e445a to b3bff31 Compare March 30, 2023 10:53
@samueltardieu
Copy link
Member Author

r? @llogiq

@rustbot rustbot assigned llogiq and unassigned flip1995 Mar 30, 2023
@llogiq
Copy link
Contributor

llogiq commented Mar 30, 2023

I wonder why we should lint stdin.lines(). The infinite loop problem only appears on reading directories, and I haven't yet been able to pipe one to stdin.

@samueltardieu
Copy link
Member Author

I wonder why we should lint stdin.lines(). The infinite loop problem only appears on reading directories, and I haven't yet been able to pipe one to stdin.

On Linux :

$ cat t.rs
fn main() {
    for l in std::io::stdin().lines() {
        println!("l = {l:?}");
    }
}
$ rustc t.rs
$ ./t < /
l = Err(Os { code: 21, kind: IsADirectory, message: "Is a directory" })
l = Err(Os { code: 21, kind: IsADirectory, message: "Is a directory" })
l = Err(Os { code: 21, kind: IsADirectory, message: "Is a directory" })
l = Err(Os { code: 21, kind: IsADirectory, message: "Is a directory" })
l = Err(Os { code: 21, kind: IsADirectory, message: "Is a directory" })
l = Err(Os { code: 21, kind: IsADirectory, message: "Is a directory" })
l = Err(Os { code: 21, kind: IsADirectory, message: "Is a directory" })
l = Err(Os { code: 21, kind: IsADirectory, message: "Is a directory" })
l = Err(Os { code: 21, kind: IsADirectory, message: "Is a directory" })
l = Err(Os { code: 21, kind: IsADirectory, message: "Is a directory" })
^C

@llogiq
Copy link
Contributor

llogiq commented Mar 30, 2023

Ok then. I think the docs could use some editing for wording, otherwise it looks ok.

@samueltardieu
Copy link
Member Author

Ok then. I think the docs could use some editing for wording, otherwise it looks ok.

I have reworked the wording, tell me if you'd change other things.

Copy link
Contributor

@llogiq llogiq left a comment

Choose a reason for hiding this comment

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

The only possible problem is that the user could check for in.is_file() manually, in which case we'd have a false positive. That said, I'd be ok with just adding that to the "known problems" section, as the code to check for this case could be quite hairy

@samueltardieu
Copy link
Member Author

Note that even if in.is_file(), an I/O error while reading the file (e.g., a USB key removed while reading) will likely persist, in which case the implementation of Lines.next() will return an Err forever (I have also verified this experimentally by creating a small fuse FS returning a read error).

I've added a "Known problems" section to the lint.

I've used "suspicious" as a category. Is that ok, or do you prefer it to be put in the "nursery"?

@llogiq
Copy link
Contributor

llogiq commented Apr 1, 2023

I think suspicious is likely fine, as the lint highlights a real problem and I haven't encountered a case where ignoring an error on one line would have produced a good outcome.

@bors r+

@bors
Copy link
Contributor

bors commented Apr 1, 2023

📌 Commit 6601d85 has been approved by llogiq

It is now in the queue for this repository.

@bors
Copy link
Contributor

bors commented Apr 1, 2023

⌛ Testing commit 6601d85 with merge ac4838c...

@bors
Copy link
Contributor

bors commented Apr 1, 2023

☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test
Approved by: llogiq
Pushing ac4838c to master...

1 similar comment
@bors
Copy link
Contributor

bors commented Apr 1, 2023

☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test
Approved by: llogiq
Pushing ac4838c to master...

@bors bors merged commit ac4838c into rust-lang:master Apr 1, 2023
@samueltardieu samueltardieu deleted the lines-filter-map-ok branch May 10, 2023 21:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants