Skip to content

Commit 6366cca

Browse files
committed
add io_other_error lint
1 parent 649cef0 commit 6366cca

File tree

14 files changed

+239
-10
lines changed

14 files changed

+239
-10
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -5726,6 +5726,7 @@ Released 2018-09-13
57265726
[`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked
57275727
[`inverted_saturating_sub`]: https://rust-lang.github.io/rust-clippy/master/index.html#inverted_saturating_sub
57285728
[`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
5729+
[`io_other_error`]: https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error
57295730
[`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
57305731
[`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
57315732
[`items_after_test_module`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_test_module

book/src/lint_configuration.md

+1
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
759759
* [`from_over_into`](https://rust-lang.github.io/rust-clippy/master/index.html#from_over_into)
760760
* [`if_then_some_else_none`](https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none)
761761
* [`index_refutable_slice`](https://rust-lang.github.io/rust-clippy/master/index.html#index_refutable_slice)
762+
* [`io_other_error`](https://rust-lang.github.io/rust-clippy/master/index.html#io_other_error)
762763
* [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map)
763764
* [`legacy_numeric_constants`](https://rust-lang.github.io/rust-clippy/master/index.html#legacy_numeric_constants)
764765
* [`lines_filter_map_ok`](https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok)

clippy_config/src/conf.rs

+1
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,7 @@ define_Conf! {
614614
from_over_into,
615615
if_then_some_else_none,
616616
index_refutable_slice,
617+
io_other_error,
617618
iter_kv_map,
618619
legacy_numeric_constants,
619620
lines_filter_map_ok,

clippy_dev/src/new_lint.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
use crate::utils::{clippy_project_root, clippy_version};
22
use indoc::{formatdoc, writedoc};
3-
use std::fmt;
43
use std::fmt::Write as _;
54
use std::fs::{self, OpenOptions};
65
use std::io::prelude::*;
7-
use std::io::{self, ErrorKind};
86
use std::path::{Path, PathBuf};
7+
use std::{fmt, io};
98

109
struct LintData<'a> {
1110
pass: &'a str,
@@ -25,7 +24,7 @@ impl<T> Context for io::Result<T> {
2524
Ok(t) => Ok(t),
2625
Err(e) => {
2726
let message = format!("{}: {e}", text.as_ref());
28-
Err(io::Error::new(ErrorKind::Other, message))
27+
Err(io::Error::other(message))
2928
},
3029
}
3130
}

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
400400
crate::methods::INEFFICIENT_TO_STRING_INFO,
401401
crate::methods::INSPECT_FOR_EACH_INFO,
402402
crate::methods::INTO_ITER_ON_REF_INFO,
403+
crate::methods::IO_OTHER_ERROR_INFO,
403404
crate::methods::IS_DIGIT_ASCII_RADIX_INFO,
404405
crate::methods::ITERATOR_STEP_BY_ZERO_INFO,
405406
crate::methods::ITER_CLONED_COLLECT_INFO,

clippy_lints/src/manual_div_ceil.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ impl_lint_pass!(ManualDivCeil => [MANUAL_DIV_CEIL]);
5959

6060
impl<'tcx> LateLintPass<'tcx> for ManualDivCeil {
6161
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
62-
if !self.msrv.meets(msrvs::DIV_CEIL) {
62+
if !self.msrv.meets(msrvs::MANUAL_DIV_CEIL) {
6363
return;
6464
}
6565

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::msrvs::{IO_ERROR_OTHER, Msrv};
3+
use rustc_errors::Applicability;
4+
use rustc_hir::{Expr, ExprKind, QPath};
5+
use rustc_lint::LateContext;
6+
7+
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args: &[Expr<'_>], msrv: &Msrv) {
8+
if let [error_kind, error] = args
9+
&& !expr.span.from_expansion()
10+
&& !error_kind.span.from_expansion()
11+
&& clippy_utils::is_expr_path_def_path(cx, path, &clippy_utils::paths::IO_ERROR_NEW)
12+
&& clippy_utils::is_expr_path_def_path(
13+
cx,
14+
clippy_utils::expr_or_init(cx, error_kind),
15+
&clippy_utils::paths::IO_ERRORKIND_OTHER,
16+
)
17+
&& let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind
18+
&& msrv.meets(IO_ERROR_OTHER)
19+
{
20+
span_lint_and_then(
21+
cx,
22+
super::IO_OTHER_ERROR,
23+
expr.span,
24+
"this can be `std::io::Error::other(_)`",
25+
|diag| {
26+
diag.multipart_suggestion_verbose(
27+
"use `std::io::Error::other`",
28+
vec![
29+
(new_segment.ident.span, "other".to_owned()),
30+
(error_kind.span.until(error.span), String::new()),
31+
],
32+
Applicability::MachineApplicable,
33+
);
34+
},
35+
);
36+
}
37+
}

clippy_lints/src/methods/mod.rs

+25
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ mod implicit_clone;
3636
mod inefficient_to_string;
3737
mod inspect_for_each;
3838
mod into_iter_on_ref;
39+
mod io_other_error;
3940
mod is_digit_ascii_radix;
4041
mod is_empty;
4142
mod iter_cloned_collect;
@@ -4461,6 +4462,28 @@ declare_clippy_lint! {
44614462
"unnecessary `iter().any()` on slices that can be replaced with `contains()`"
44624463
}
44634464

4465+
declare_clippy_lint! {
4466+
/// This lint warns on calling `io::Error::new(..)` with a kind of
4467+
/// `io::ErrorKind::Other`.
4468+
///
4469+
/// ### Why is this bad?
4470+
/// Since Rust 1.74, there's the `io::Error::other(_)` shortcut.
4471+
///
4472+
/// ### Example
4473+
/// ```no_run
4474+
/// use std::io;
4475+
/// let _ = io::Error::new(io::ErrorKind::Other, "bad".to_string());
4476+
/// ```
4477+
/// Use instead:
4478+
/// ```no_run
4479+
/// let _ = std::io::Error::other("bad".to_string());
4480+
/// ```
4481+
#[clippy::version = "1.86.0"]
4482+
pub IO_OTHER_ERROR,
4483+
style,
4484+
"calling `std::io::Error::new(std::io::ErrorKind::Other, _)`"
4485+
}
4486+
44644487
#[expect(clippy::struct_excessive_bools)]
44654488
pub struct Methods {
44664489
avoid_breaking_exported_api: bool,
@@ -4637,6 +4660,7 @@ impl_lint_pass!(Methods => [
46374660
RETURN_AND_THEN,
46384661
UNBUFFERED_BYTES,
46394662
MANUAL_CONTAINS,
4663+
IO_OTHER_ERROR,
46404664
]);
46414665

46424666
/// Extracts a method call name, args, and `Span` of the method name.
@@ -4666,6 +4690,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
46664690
unnecessary_fallible_conversions::check_function(cx, expr, func);
46674691
manual_c_str_literals::check(cx, expr, func, args, &self.msrv);
46684692
useless_nonzero_new_unchecked::check(cx, expr, func, args, &self.msrv);
4693+
io_other_error::check(cx, expr, func, args, &self.msrv);
46694694
},
46704695
ExprKind::MethodCall(method_call, receiver, args, _) => {
46714696
let method_span = method_call.ident.span;

clippy_utils/src/msrvs.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,8 @@ msrv_aliases! {
2727
1,77,0 { C_STR_LITERALS }
2828
1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }
2929
1,75,0 { OPTION_AS_SLICE }
30-
1,74,0 { REPR_RUST }
31-
1,73,0 { DIV_CEIL }
30+
1,74,0 { REPR_RUST, IO_ERROR_OTHER }
31+
1,73,0 { MANUAL_DIV_CEIL }
3232
1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
3333
1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN }
3434
1,68,0 { PATH_MAIN_SEPARATOR_STR }

clippy_utils/src/paths.rs

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]
2929

3030
// Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items.
3131
pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "<impl char>", "is_ascii"];
32+
pub const IO_ERROR_NEW: [&str; 5] = ["std", "io", "error", "Error", "new"];
33+
pub const IO_ERRORKIND_OTHER: [&str; 5] = ["std", "io", "error", "ErrorKind", "Other"];
3234

3335
// Paths in clippy itself
3436
pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"];

tests/ui/format_args_unfixable.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
#![allow(unused)]
33
#![allow(clippy::assertions_on_constants, clippy::eq_op, clippy::uninlined_format_args)]
44

5-
use std::io::{Error, ErrorKind, Write, stdout};
5+
use std::io::{Error, Write, stdout};
66
use std::ops::Deref;
77
use std::panic::Location;
88

@@ -20,7 +20,7 @@ macro_rules! my_other_macro {
2020
}
2121

2222
fn main() {
23-
let error = Error::new(ErrorKind::Other, "bad thing");
23+
let error = Error::other("bad thing");
2424
let x = 'x';
2525

2626
println!("error: {}", format!("something failed at {}", Location::caller()));
@@ -115,7 +115,7 @@ macro_rules! my_println2_args {
115115
}
116116

117117
fn test2() {
118-
let error = Error::new(ErrorKind::Other, "bad thing");
118+
let error = Error::other("bad thing");
119119

120120
// None of these should be linted without the config change
121121
my_println2!(true, "error: {}", format!("something failed at {}", Location::caller()));
@@ -145,7 +145,7 @@ macro_rules! usr_println {
145145
}
146146

147147
fn user_format() {
148-
let error = Error::new(ErrorKind::Other, "bad thing");
148+
let error = Error::other("bad thing");
149149
let x = 'x';
150150

151151
usr_println!(true, "error: {}", format!("boom at {}", Location::caller()));

tests/ui/io_other_error.fixed

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#![warn(clippy::io_other_error)]
2+
use std::fmt;
3+
4+
#[derive(Debug)]
5+
struct E;
6+
7+
impl std::error::Error for E {}
8+
impl fmt::Display for E {
9+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10+
f.write_str("E")
11+
}
12+
}
13+
14+
macro_rules! o {
15+
{} => { std::io::ErrorKind::Other };
16+
}
17+
18+
macro_rules! e {
19+
{ $kind:expr } => { std::io::Error::new($kind, E) };
20+
}
21+
22+
fn main() {
23+
let _err = std::io::Error::other(E);
24+
//~^ ERROR: this can be `std::io::Error::other(_)`
25+
let other = std::io::ErrorKind::Other;
26+
let _err = std::io::Error::other(E);
27+
//~^ ERROR: this can be `std::io::Error::other(_)`
28+
29+
// not other
30+
let _err = std::io::Error::new(std::io::ErrorKind::TimedOut, E);
31+
32+
// from expansion
33+
let _err = e!(other);
34+
let _err = std::io::Error::new(o!(), E);
35+
let _err = e!(o!());
36+
37+
paths::short();
38+
under_msrv();
39+
}
40+
41+
mod paths {
42+
use std::io::{self, Error, ErrorKind};
43+
44+
pub fn short() {
45+
let _err = Error::other(super::E);
46+
//~^ ERROR: this can be `std::io::Error::other(_)`
47+
let _err = io::Error::other(super::E);
48+
//~^ ERROR: this can be `std::io::Error::other(_)`
49+
}
50+
}
51+
52+
#[clippy::msrv = "1.73"]
53+
fn under_msrv() {
54+
let _err = std::io::Error::new(std::io::ErrorKind::Other, E);
55+
}

tests/ui/io_other_error.rs

+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#![warn(clippy::io_other_error)]
2+
use std::fmt;
3+
4+
#[derive(Debug)]
5+
struct E;
6+
7+
impl std::error::Error for E {}
8+
impl fmt::Display for E {
9+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
10+
f.write_str("E")
11+
}
12+
}
13+
14+
macro_rules! o {
15+
{} => { std::io::ErrorKind::Other };
16+
}
17+
18+
macro_rules! e {
19+
{ $kind:expr } => { std::io::Error::new($kind, E) };
20+
}
21+
22+
fn main() {
23+
let _err = std::io::Error::new(std::io::ErrorKind::Other, E);
24+
//~^ ERROR: this can be `std::io::Error::other(_)`
25+
let other = std::io::ErrorKind::Other;
26+
let _err = std::io::Error::new(other, E);
27+
//~^ ERROR: this can be `std::io::Error::other(_)`
28+
29+
// not other
30+
let _err = std::io::Error::new(std::io::ErrorKind::TimedOut, E);
31+
32+
// from expansion
33+
let _err = e!(other);
34+
let _err = std::io::Error::new(o!(), E);
35+
let _err = e!(o!());
36+
37+
paths::short();
38+
under_msrv();
39+
}
40+
41+
mod paths {
42+
use std::io::{self, Error, ErrorKind};
43+
44+
pub fn short() {
45+
let _err = Error::new(ErrorKind::Other, super::E);
46+
//~^ ERROR: this can be `std::io::Error::other(_)`
47+
let _err = io::Error::new(io::ErrorKind::Other, super::E);
48+
//~^ ERROR: this can be `std::io::Error::other(_)`
49+
}
50+
}
51+
52+
#[clippy::msrv = "1.73"]
53+
fn under_msrv() {
54+
let _err = std::io::Error::new(std::io::ErrorKind::Other, E);
55+
}

tests/ui/io_other_error.stderr

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
error: this can be `std::io::Error::other(_)`
2+
--> tests/ui/io_other_error.rs:23:16
3+
|
4+
LL | let _err = std::io::Error::new(std::io::ErrorKind::Other, E);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
|
7+
= note: `-D clippy::io-other-error` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::io_other_error)]`
9+
help: use `std::io::Error::other`
10+
|
11+
LL - let _err = std::io::Error::new(std::io::ErrorKind::Other, E);
12+
LL + let _err = std::io::Error::other(E);
13+
|
14+
15+
error: this can be `std::io::Error::other(_)`
16+
--> tests/ui/io_other_error.rs:26:16
17+
|
18+
LL | let _err = std::io::Error::new(other, E);
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20+
|
21+
help: use `std::io::Error::other`
22+
|
23+
LL - let _err = std::io::Error::new(other, E);
24+
LL + let _err = std::io::Error::other(E);
25+
|
26+
27+
error: this can be `std::io::Error::other(_)`
28+
--> tests/ui/io_other_error.rs:45:20
29+
|
30+
LL | let _err = Error::new(ErrorKind::Other, super::E);
31+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
32+
|
33+
help: use `std::io::Error::other`
34+
|
35+
LL - let _err = Error::new(ErrorKind::Other, super::E);
36+
LL + let _err = Error::other(super::E);
37+
|
38+
39+
error: this can be `std::io::Error::other(_)`
40+
--> tests/ui/io_other_error.rs:47:20
41+
|
42+
LL | let _err = io::Error::new(io::ErrorKind::Other, super::E);
43+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44+
|
45+
help: use `std::io::Error::other`
46+
|
47+
LL - let _err = io::Error::new(io::ErrorKind::Other, super::E);
48+
LL + let _err = io::Error::other(super::E);
49+
|
50+
51+
error: aborting due to 4 previous errors
52+

0 commit comments

Comments
 (0)