Skip to content

Commit 7182a6b

Browse files
committed
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 #7886. This PR is related to #9667, so I will update `methods/mod.rs` if it get conflicted.
2 parents 634987b + 6efb3a2 commit 7182a6b

File tree

9 files changed

+162
-1
lines changed

9 files changed

+162
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4200,6 +4200,7 @@ Released 2018-09-13
42004200
[`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
42014201
[`same_name_method`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_name_method
42024202
[`search_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#search_is_some
4203+
[`seek_from_current`]: https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current
42034204
[`seek_to_start_instead_of_rewind`]: https://rust-lang.github.io/rust-clippy/master/index.html#seek_to_start_instead_of_rewind
42044205
[`self_assignment`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_assignment
42054206
[`self_named_constructors`]: https://rust-lang.github.io/rust-clippy/master/index.html#self_named_constructors

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
363363
crate::methods::REPEAT_ONCE_INFO,
364364
crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO,
365365
crate::methods::SEARCH_IS_SOME_INFO,
366+
crate::methods::SEEK_FROM_CURRENT_INFO,
366367
crate::methods::SEEK_TO_START_INSTEAD_OF_REWIND_INFO,
367368
crate::methods::SHOULD_IMPLEMENT_TRAIT_INFO,
368369
crate::methods::SINGLE_CHAR_ADD_STR_INFO,

clippy_lints/src/methods/mod.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ mod path_buf_push_overwrite;
6969
mod range_zip_with_len;
7070
mod repeat_once;
7171
mod search_is_some;
72+
mod seek_from_current;
7273
mod seek_to_start_instead_of_rewind;
7374
mod single_char_add_str;
7475
mod single_char_insert_string;
@@ -3067,6 +3068,49 @@ declare_clippy_lint! {
30673068
"iterating on map using `iter` when `keys` or `values` would do"
30683069
}
30693070

3071+
declare_clippy_lint! {
3072+
/// ### What it does
3073+
///
3074+
/// Checks an argument of `seek` method of `Seek` trait
3075+
/// and if it start seek from `SeekFrom::Current(0)`, suggests `stream_position` instead.
3076+
///
3077+
/// ### Why is this bad?
3078+
///
3079+
/// Readability. Use dedicated method.
3080+
///
3081+
/// ### Example
3082+
///
3083+
/// ```rust
3084+
/// use std::fs::File;
3085+
/// use std::io::{self, Write, Seek, SeekFrom};
3086+
///
3087+
/// fn main() -> io::Result<()> {
3088+
/// let mut f = File::create("foo.txt")?;
3089+
/// f.write_all(b"Hello")?;
3090+
/// eprintln!("Written {} bytes", f.seek(SeekFrom::Current(0))?);
3091+
///
3092+
/// Ok(())
3093+
/// }
3094+
/// ```
3095+
/// Use instead:
3096+
/// ```rust
3097+
/// use std::fs::File;
3098+
/// use std::io::{self, Write, Seek, SeekFrom};
3099+
///
3100+
/// fn main() -> io::Result<()> {
3101+
/// let mut f = File::create("foo.txt")?;
3102+
/// f.write_all(b"Hello")?;
3103+
/// eprintln!("Written {} bytes", f.stream_position()?);
3104+
///
3105+
/// Ok(())
3106+
/// }
3107+
/// ```
3108+
#[clippy::version = "1.66.0"]
3109+
pub SEEK_FROM_CURRENT,
3110+
complexity,
3111+
"use dedicated method for seek from current position"
3112+
}
3113+
30703114
declare_clippy_lint! {
30713115
/// ### What it does
30723116
///
@@ -3222,6 +3266,7 @@ impl_lint_pass!(Methods => [
32223266
VEC_RESIZE_TO_ZERO,
32233267
VERBOSE_FILE_READS,
32243268
ITER_KV_MAP,
3269+
SEEK_FROM_CURRENT,
32253270
SEEK_TO_START_INSTEAD_OF_REWIND,
32263271
]);
32273272

@@ -3638,6 +3683,9 @@ impl Methods {
36383683
vec_resize_to_zero::check(cx, expr, count_arg, default_arg, span);
36393684
},
36403685
("seek", [arg]) => {
3686+
if meets_msrv(self.msrv, msrvs::SEEK_FROM_CURRENT) {
3687+
seek_from_current::check(cx, expr, recv, arg);
3688+
}
36413689
if meets_msrv(self.msrv, msrvs::SEEK_REWIND) {
36423690
seek_to_start_instead_of_rewind::check(cx, expr, recv, arg, span);
36433691
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
use rustc_ast::ast::{LitIntType, LitKind};
2+
use rustc_errors::Applicability;
3+
use rustc_hir::{Expr, ExprKind};
4+
use rustc_lint::LateContext;
5+
6+
use clippy_utils::{
7+
diagnostics::span_lint_and_sugg, get_trait_def_id, match_def_path, paths, source::snippet_with_applicability,
8+
ty::implements_trait,
9+
};
10+
11+
use super::SEEK_FROM_CURRENT;
12+
13+
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &'tcx Expr<'_>, arg: &'tcx Expr<'_>) {
14+
let ty = cx.typeck_results().expr_ty(recv);
15+
16+
if let Some(def_id) = get_trait_def_id(cx, &paths::STD_IO_SEEK) {
17+
if implements_trait(cx, ty, def_id, &[]) && arg_is_seek_from_current(cx, arg) {
18+
let mut applicability = Applicability::MachineApplicable;
19+
let snip = snippet_with_applicability(cx, recv.span, "..", &mut applicability);
20+
21+
span_lint_and_sugg(
22+
cx,
23+
SEEK_FROM_CURRENT,
24+
expr.span,
25+
"using `SeekFrom::Current` to start from current position",
26+
"replace with",
27+
format!("{snip}.stream_position()"),
28+
applicability,
29+
);
30+
}
31+
}
32+
}
33+
34+
fn arg_is_seek_from_current<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
35+
if let ExprKind::Call(f, args) = expr.kind &&
36+
let ExprKind::Path(ref path) = f.kind &&
37+
let Some(def_id) = cx.qpath_res(path, f.hir_id).opt_def_id() &&
38+
match_def_path(cx, def_id, &paths::STD_IO_SEEK_FROM_CURRENT) {
39+
// check if argument of `SeekFrom::Current` is `0`
40+
if args.len() == 1 &&
41+
let ExprKind::Lit(ref lit) = args[0].kind &&
42+
let LitKind::Int(0, LitIntType::Unsuffixed) = lit.node {
43+
return true
44+
}
45+
}
46+
47+
false
48+
}

clippy_utils/src/msrvs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ msrv_aliases! {
1717
1,58,0 { FORMAT_ARGS_CAPTURE }
1818
1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
1919
1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
20-
1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS }
20+
1,51,0 { BORROW_AS_PTR, SEEK_FROM_CURRENT, UNSIGNED_ABS }
2121
1,50,0 { BOOL_THEN, CLAMP }
2222
1,47,0 { TAU }
2323
1,46,0 { CONST_IF_MATCH }

clippy_utils/src/paths.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
116116
pub const CONVERT_IDENTITY: [&str; 3] = ["core", "convert", "identity"];
117117
pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
118118
pub const STD_IO_SEEK: [&str; 3] = ["std", "io", "Seek"];
119+
pub const STD_IO_SEEK_FROM_CURRENT: [&str; 4] = ["std", "io", "SeekFrom", "Current"];
119120
pub const STD_IO_SEEKFROM_START: [&str; 4] = ["std", "io", "SeekFrom", "Start"];
120121
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
121122
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];

tests/ui/seek_from_current.fixed

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// run-rustfix
2+
#![warn(clippy::seek_from_current)]
3+
#![feature(custom_inner_attributes)]
4+
5+
use std::fs::File;
6+
use std::io::{self, Seek, SeekFrom, Write};
7+
8+
fn _msrv_1_50() -> io::Result<()> {
9+
#![clippy::msrv = "1.50"]
10+
let mut f = File::create("foo.txt")?;
11+
f.write_all(b"Hi!")?;
12+
f.seek(SeekFrom::Current(0))?;
13+
f.seek(SeekFrom::Current(1))?;
14+
Ok(())
15+
}
16+
17+
fn _msrv_1_51() -> io::Result<()> {
18+
#![clippy::msrv = "1.51"]
19+
let mut f = File::create("foo.txt")?;
20+
f.write_all(b"Hi!")?;
21+
f.stream_position()?;
22+
f.seek(SeekFrom::Current(1))?;
23+
Ok(())
24+
}
25+
26+
fn main() {}

tests/ui/seek_from_current.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// run-rustfix
2+
#![warn(clippy::seek_from_current)]
3+
#![feature(custom_inner_attributes)]
4+
5+
use std::fs::File;
6+
use std::io::{self, Seek, SeekFrom, Write};
7+
8+
fn _msrv_1_50() -> io::Result<()> {
9+
#![clippy::msrv = "1.50"]
10+
let mut f = File::create("foo.txt")?;
11+
f.write_all(b"Hi!")?;
12+
f.seek(SeekFrom::Current(0))?;
13+
f.seek(SeekFrom::Current(1))?;
14+
Ok(())
15+
}
16+
17+
fn _msrv_1_51() -> io::Result<()> {
18+
#![clippy::msrv = "1.51"]
19+
let mut f = File::create("foo.txt")?;
20+
f.write_all(b"Hi!")?;
21+
f.seek(SeekFrom::Current(0))?;
22+
f.seek(SeekFrom::Current(1))?;
23+
Ok(())
24+
}
25+
26+
fn main() {}

tests/ui/seek_from_current.stderr

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: using `SeekFrom::Current` to start from current position
2+
--> $DIR/seek_from_current.rs:21:5
3+
|
4+
LL | f.seek(SeekFrom::Current(0))?;
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `f.stream_position()`
6+
|
7+
= note: `-D clippy::seek-from-current` implied by `-D warnings`
8+
9+
error: aborting due to previous error
10+

0 commit comments

Comments
 (0)