Auto merge of #9681 - koka831:feat/add-seek-from-current-lint, r=giraffate
feat: add new lint `seek_from_current` changelog: `seek_from_current`: new lint to suggest using `stream_position` instead of seek from current position with `SeekFrom::Current(0)` addresses https://github.com/rust-lang/rust-clippy/issues/7886. This PR is related to https://github.com/rust-lang/rust-clippy/pull/9667, so I will update `methods/mod.rs` if it get conflicted.
This commit is contained in:
commit
7182a6ba0d
@ -4200,6 +4200,7 @@ Released 2018-09-13
|
||||
[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
|
||||
[`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method
|
||||
[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
|
||||
[`seek_from_current`]: https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current
|
||||
[`seek_to_start_instead_of_rewind`]: https://rust-lang.github.io/rust-clippy/master/index.html#seek_to_start_instead_of_rewind
|
||||
[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
|
||||
[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors
|
||||
|
@ -363,6 +363,7 @@
|
||||
crate::methods::REPEAT_ONCE_INFO,
|
||||
crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO,
|
||||
crate::methods::SEARCH_IS_SOME_INFO,
|
||||
crate::methods::SEEK_FROM_CURRENT_INFO,
|
||||
crate::methods::SEEK_TO_START_INSTEAD_OF_REWIND_INFO,
|
||||
crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO,
|
||||
crate::methods::SINGLE_CHAR_ADD_STR_INFO,
|
||||
|
@ -69,6 +69,7 @@
|
||||
mod range_zip_with_len;
|
||||
mod repeat_once;
|
||||
mod search_is_some;
|
||||
mod seek_from_current;
|
||||
mod seek_to_start_instead_of_rewind;
|
||||
mod single_char_add_str;
|
||||
mod single_char_insert_string;
|
||||
@ -3067,6 +3068,49 @@
|
||||
"iterating on map using `iter` when `keys` or `values` would do"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
/// Checks an argument of `seek` method of `Seek` trait
|
||||
/// and if it start seek from `SeekFrom::Current(0)`, suggests `stream_position` instead.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
///
|
||||
/// Readability. Use dedicated method.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::fs::File;
|
||||
/// use std::io::{self, Write, Seek, SeekFrom};
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut f = File::create("foo.txt")?;
|
||||
/// f.write_all(b"Hello")?;
|
||||
/// eprintln!("Written {} bytes", f.seek(SeekFrom::Current(0))?);
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
/// Use instead:
|
||||
/// ```rust
|
||||
/// use std::fs::File;
|
||||
/// use std::io::{self, Write, Seek, SeekFrom};
|
||||
///
|
||||
/// fn main() -> io::Result<()> {
|
||||
/// let mut f = File::create("foo.txt")?;
|
||||
/// f.write_all(b"Hello")?;
|
||||
/// eprintln!("Written {} bytes", f.stream_position()?);
|
||||
///
|
||||
/// Ok(())
|
||||
/// }
|
||||
/// ```
|
||||
#[clippy::version = "1.66.0"]
|
||||
pub SEEK_FROM_CURRENT,
|
||||
complexity,
|
||||
"use dedicated method for seek from current position"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
///
|
||||
@ -3222,6 +3266,7 @@ pub fn new(
|
||||
VEC_RESIZE_TO_ZERO,
|
||||
VERBOSE_FILE_READS,
|
||||
ITER_KV_MAP,
|
||||
SEEK_FROM_CURRENT,
|
||||
SEEK_TO_START_INSTEAD_OF_REWIND,
|
||||
]);
|
||||
|
||||
@ -3638,6 +3683,9 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
|
||||
vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
|
||||
},
|
||||
("seek", [arg]) => {
|
||||
if meets_msrv(self.msrv, msrvs::SEEK_FROM_CURRENT) {
|
||||
seek_from_current::check(cx, expr, recv, arg);
|
||||
}
|
||||
if meets_msrv(self.msrv, msrvs::SEEK_REWIND) {
|
||||
seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span);
|
||||
}
|
||||
|
48
clippy_lints/src/methods/seek_from_current.rs
Normal file
48
clippy_lints/src/methods/seek_from_current.rs
Normal file
@ -0,0 +1,48 @@
|
||||
use rustc_ast::ast::{LitIntType, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
|
||||
use clippy_utils::{
|
||||
diagnostics::span_lint_and_sugg, get_trait_def_id, match_def_path, paths, source::snippet_with_applicability,
|
||||
ty::implements_trait,
|
||||
};
|
||||
|
||||
use super::SEEK_FROM_CURRENT;
|
||||
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
|
||||
let ty = cx.typeck_results().expr_ty(recv);
|
||||
|
||||
if let Some(def_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) {
|
||||
if implements_trait(cx, ty, def_id, &[]) && arg_is_seek_from_current(cx, arg) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability);
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
SEEK_FROM_CURRENT,
|
||||
expr.span,
|
||||
"using `SeekFrom::Current` to start from current position",
|
||||
"replace with",
|
||||
format!("{snip}.stream_position()"),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
|
||||
if let ExprKind::Call(f, args) = expr.kind &&
|
||||
let ExprKind::Path(ref path) = f.kind &&
|
||||
let Some(def_id) = cx.qpath_res(path, f.hir_id).opt_def_id() &&
|
||||
match_def_path(cx, def_id, &paths::STD_IO_SEEK_FROM_CURRENT) {
|
||||
// check if argument of `SeekFrom::Current` is `0`
|
||||
if args.len() == 1 &&
|
||||
let ExprKind::Lit(ref lit) = args[0].kind &&
|
||||
let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
@ -17,7 +17,7 @@ macro_rules! msrv_aliases {
|
||||
1,58,0 { FORMAT_ARGS_CAPTURE }
|
||||
1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
|
||||
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
|
||||
1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS }
|
||||
1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
|
||||
1,50,0 { BOOL_THEN, CLAMP }
|
||||
1,47,0 { TAU }
|
||||
1,46,0 { CONST_IF_MATCH }
|
||||
|
@ -116,6 +116,7 @@
|
||||
pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
|
||||
pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
|
||||
pub const STD_IO_SEEK: [&str; 3] = ["std", "io", "Seek"];
|
||||
pub const STD_IO_SEEK_FROM_CURRENT: [&str; 4] = ["std", "io", "SeekFrom", "Current"];
|
||||
pub const STD_IO_SEEKFROM_START: [&str; 4] = ["std", "io", "SeekFrom", "Start"];
|
||||
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
|
||||
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
|
||||
|
26
tests/ui/seek_from_current.fixed
Normal file
26
tests/ui/seek_from_current.fixed
Normal file
@ -0,0 +1,26 @@
|
||||
// run-rustfix
|
||||
#![warn(clippy::seek_from_current)]
|
||||
#![feature(custom_inner_attributes)]
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{self, Seek, SeekFrom, Write};
|
||||
|
||||
fn _msrv_1_50() -> io::Result<()> {
|
||||
#![clippy::msrv = "1.50"]
|
||||
let mut f = File::create("foo.txt")?;
|
||||
f.write_all(b"Hi!")?;
|
||||
f.seek(SeekFrom::Current(0))?;
|
||||
f.seek(SeekFrom::Current(1))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn _msrv_1_51() -> io::Result<()> {
|
||||
#![clippy::msrv = "1.51"]
|
||||
let mut f = File::create("foo.txt")?;
|
||||
f.write_all(b"Hi!")?;
|
||||
f.stream_position()?;
|
||||
f.seek(SeekFrom::Current(1))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {}
|
26
tests/ui/seek_from_current.rs
Normal file
26
tests/ui/seek_from_current.rs
Normal file
@ -0,0 +1,26 @@
|
||||
// run-rustfix
|
||||
#![warn(clippy::seek_from_current)]
|
||||
#![feature(custom_inner_attributes)]
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{self, Seek, SeekFrom, Write};
|
||||
|
||||
fn _msrv_1_50() -> io::Result<()> {
|
||||
#![clippy::msrv = "1.50"]
|
||||
let mut f = File::create("foo.txt")?;
|
||||
f.write_all(b"Hi!")?;
|
||||
f.seek(SeekFrom::Current(0))?;
|
||||
f.seek(SeekFrom::Current(1))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn _msrv_1_51() -> io::Result<()> {
|
||||
#![clippy::msrv = "1.51"]
|
||||
let mut f = File::create("foo.txt")?;
|
||||
f.write_all(b"Hi!")?;
|
||||
f.seek(SeekFrom::Current(0))?;
|
||||
f.seek(SeekFrom::Current(1))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {}
|
10
tests/ui/seek_from_current.stderr
Normal file
10
tests/ui/seek_from_current.stderr
Normal file
@ -0,0 +1,10 @@
|
||||
error: using `SeekFrom::Current` to start from current position
|
||||
--> $DIR/seek_from_current.rs:21:5
|
||||
|
|
||||
LL | f.seek(SeekFrom::Current(0))?;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `f.stream_position()`
|
||||
|
|
||||
= note: `-D clippy::seek-from-current` implied by `-D warnings`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
Loading…
Reference in New Issue
Block a user