diff --git a/CHANGELOG.md b/CHANGELOG.md index 161fa630ed47..91c7b30cdfc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5367,6 +5367,7 @@ Released 2018-09-13 [`box_vec`]: https://rust-lang.github.io/rust-clippy/master/index.html#box_vec [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code +[`bufreader_stdin`]: https://rust-lang.github.io/rust-clippy/master/index.html#bufreader_stdin [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow [`byte_char_slices`]: https://rust-lang.github.io/rust-clippy/master/index.html#byte_char_slices [`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len diff --git a/clippy_lints/src/bufreader_stdin.rs b/clippy_lints/src/bufreader_stdin.rs new file mode 100644 index 000000000000..f2134d99428f --- /dev/null +++ b/clippy_lints/src/bufreader_stdin.rs @@ -0,0 +1,73 @@ +use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::match_def_path; +use clippy_utils::source::snippet; +use rustc_errors::Applicability; +use rustc_hir::{ExprKind, QPath, TyKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::sym; + +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `BufReader::new` with `Stdin` or `StdinLock`. + /// + /// ### Why is this bad? + /// `Stdin` is already buffered. Re-buffering it only increase the memcpy calls. + /// + /// ### Example + /// ```no_run + /// let reader = std::io::BufReader::new(std::io::stdin()); + /// ``` + /// Use instead: + /// ```no_run + /// let stdin = std::io::stdin(); + /// let reader = stdin.lock(); + /// ``` + #[clippy::version = "1.84.0"] + pub BUFREADER_STDIN, + perf, + "using `BufReader::new` with `Stdin` or `StdinLock` is unnecessary and less efficient" +} + +declare_lint_pass!(BufreaderStdinlock => [BUFREADER_STDIN]); + +impl<'tcx> LateLintPass<'tcx> for BufreaderStdinlock { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) { + if e.span.from_expansion() { + return; + } + if let ExprKind::Call(func, [arg]) = e.kind + && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = func.kind + && seg.ident.name == sym::new + && let TyKind::Path(ref qpath) = ty.kind + && let Some(did) = cx.qpath_res(qpath, ty.hir_id).opt_def_id() + && match_def_path(cx, did, &["std", "io", "buffered", "bufreader", "BufReader"]) + && let arg_ty = cx.typeck_results().expr_ty(arg) + && let Some(arg_did) = arg_ty.ty_adt_def().map(rustc_middle::ty::AdtDef::did) + { + if match_def_path(cx, arg_did, &["std", "io", "stdio", "Stdin"]) { + span_lint_and_sugg( + cx, + BUFREADER_STDIN, + e.span, + "using `BufReader::new` with `Stdin`", + "instead of wrapping `Stdin` in `BufReader`, use the `lock` method directly", + format!("{}.lock()", snippet(cx, arg.span, "..")), + Applicability::MachineApplicable, + ); + } else if match_def_path(cx, arg_did, &["std", "io", "stdio", "StdinLock"]) { + span_lint_and_sugg( + cx, + BUFREADER_STDIN, + e.span, + "using `BufReader::new` with `Stdin`", + "instead of wrapping `StdinLock` in `BufReader`, use it self", + snippet(cx, arg.span, "..").to_string(), + Applicability::MachineApplicable, + ); + } else { + return; + }; + } + } +} diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs index edb52851e0cb..29225fe90504 100644 --- a/clippy_lints/src/declared_lints.rs +++ b/clippy_lints/src/declared_lints.rs @@ -68,6 +68,7 @@ pub static LINTS: &[&crate::LintInfo] = &[ crate::booleans::OVERLY_COMPLEX_BOOL_EXPR_INFO, crate::borrow_deref_ref::BORROW_DEREF_REF_INFO, crate::box_default::BOX_DEFAULT_INFO, + crate::bufreader_stdin::BUFREADER_STDIN_INFO, crate::byte_char_slices::BYTE_CHAR_SLICES_INFO, crate::cargo::CARGO_COMMON_METADATA_INFO, crate::cargo::LINT_GROUPS_PRIORITY_INFO, diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index c9064df25ac8..7ac1a93542c8 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -89,6 +89,7 @@ mod bool_to_int_with_if; mod booleans; mod borrow_deref_ref; mod box_default; +mod bufreader_stdin; mod byte_char_slices; mod cargo; mod casts; @@ -963,5 +964,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp)); store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound)); store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf))); + store.register_late_pass(|_| Box::new(bufreader_stdin::BufreaderStdinlock)); // add lints here, do not remove this comment, it's used in `new_lint` } diff --git a/tests/ui/bufreader_stdin.fixed b/tests/ui/bufreader_stdin.fixed new file mode 100644 index 000000000000..2c245381b488 --- /dev/null +++ b/tests/ui/bufreader_stdin.fixed @@ -0,0 +1,10 @@ +#![warn(clippy::bufreader_stdin)] +use std::io::{self, BufReader}; + +fn main() { + let a = io::stdin(); + let reader = a.lock(); + + let b = io::stdin().lock(); + let reader = b; +} diff --git a/tests/ui/bufreader_stdin.rs b/tests/ui/bufreader_stdin.rs new file mode 100644 index 000000000000..10c85372adfd --- /dev/null +++ b/tests/ui/bufreader_stdin.rs @@ -0,0 +1,10 @@ +#![warn(clippy::BUFREADER_STDIN)] +use std::io::{self, BufReader}; + +fn main() { + let a = io::stdin(); + let reader = BufReader::new(a); + + let b = io::stdin().lock(); + let reader = BufReader::new(b); +} diff --git a/tests/ui/bufreader_stdin.stderr b/tests/ui/bufreader_stdin.stderr new file mode 100644 index 000000000000..176e476158d6 --- /dev/null +++ b/tests/ui/bufreader_stdin.stderr @@ -0,0 +1,30 @@ +error: unknown lint: `clippy::BUFREADER_STDIN` + --> tests/ui/bufreader_stdin.rs:1:9 + | +LL | #![warn(clippy::BUFREADER_STDIN)] + | ^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean: `clippy::bufreader_stdin` + | + = note: `-D unknown-lints` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(unknown_lints)]` + +error: using `BufReader::new` with `Stdin` + --> tests/ui/bufreader_stdin.rs:6:18 + | +LL | let reader = BufReader::new(a); + | ^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::bufreader-stdin` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::bufreader_stdin)]` +help: instead of wrapping `Stdin` in `BufReader`, use the `lock` method directly + | +LL | let reader = a.lock(); + | ~~~~~~~~ + +error: using `BufReader::new` with `Stdin` + --> tests/ui/bufreader_stdin.rs:9:18 + | +LL | let reader = BufReader::new(b); + | ^^^^^^^^^^^^^^^^^ help: instead of wrapping `StdinLock` in `BufReader`, use it self: `b` + +error: aborting due to 3 previous errors +