Skip to content

Commit ec11bb6

Browse files
committed
New lint to detect &std::path::MAIN_SEPARATOR.to_string()
1 parent 8e1dd06 commit ec11bb6

9 files changed

+116
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4661,6 +4661,7 @@ Released 2018-09-13
46614661
[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
46624662
[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
46634663
[`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else
4664+
[`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str
46644665
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
46654666
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
46664667
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive

clippy_lints/src/declared_lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
262262
crate::manual_clamp::MANUAL_CLAMP_INFO,
263263
crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
264264
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
265+
crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO,
265266
crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
266267
crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO,
267268
crate::manual_retain::MANUAL_RETAIN_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ mod manual_bits;
179179
mod manual_clamp;
180180
mod manual_is_ascii_check;
181181
mod manual_let_else;
182+
mod manual_main_separator_str;
182183
mod manual_non_exhaustive;
183184
mod manual_rem_euclid;
184185
mod manual_retain;
@@ -933,6 +934,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
933934
store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
934935
store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock));
935936
store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
937+
store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(msrv())));
936938
// add lints here, do not remove this comment, it's used in `new_lint`
937939
}
938940

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::msrvs::{self, Msrv};
3+
use clippy_utils::{is_trait_method, match_def_path, paths};
4+
use rustc_errors::Applicability;
5+
use rustc_hir::def::{DefKind, Res};
6+
use rustc_hir::*;
7+
use rustc_lint::{LateContext, LateLintPass};
8+
use rustc_middle::ty;
9+
use rustc_session::{declare_tool_lint, impl_lint_pass};
10+
use rustc_span::sym;
11+
12+
declare_clippy_lint! {
13+
/// ### What it does
14+
/// Checks for references on `std::path::MAIN_SEPARATOR.to_string()` used
15+
/// to build a `&str`.
16+
///
17+
/// ### Why is this bad?
18+
/// There exists a `std::path::MAIN_SEPARATOR_STR` which does not require
19+
/// an extra memory allocation.
20+
///
21+
/// ### Example
22+
/// ```rust
23+
/// let s: &str = &std::path::MAIN_SEPARATOR.to_string();
24+
/// ```
25+
/// Use instead:
26+
/// ```rust
27+
/// let s: &str = std::path::MAIN_SEPARATOR_STR;
28+
/// ```
29+
#[clippy::version = "1.70.0"]
30+
pub MANUAL_MAIN_SEPARATOR_STR,
31+
complexity,
32+
"`&std::path::MAIN_SEPARATOR.to_string()` can be replaced by `std::path::MAIN_SEPARATOR_STR`"
33+
}
34+
35+
pub struct ManualMainSeparatorStr {
36+
msrv: Msrv,
37+
}
38+
39+
impl ManualMainSeparatorStr {
40+
#[must_use]
41+
pub fn new(msrv: Msrv) -> Self {
42+
Self { msrv }
43+
}
44+
}
45+
46+
impl_lint_pass!(ManualMainSeparatorStr => [MANUAL_MAIN_SEPARATOR_STR]);
47+
48+
impl<'tcx> LateLintPass<'tcx> for ManualMainSeparatorStr {
49+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
50+
if self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR) &&
51+
let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, target) = expr.kind &&
52+
is_trait_method(cx, target, sym::ToString) &&
53+
let ExprKind::MethodCall(path, receiver, &[], _) = target.kind &&
54+
path.ident.name == sym::to_string &&
55+
let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind &&
56+
let Res::Def(DefKind::Const, receiver_def_id) = path.res &&
57+
match_def_path(cx, receiver_def_id, &paths::PATH_MAIN_SEPARATOR) &&
58+
let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind() &&
59+
ty.is_str()
60+
{
61+
span_lint_and_sugg(
62+
cx,
63+
MANUAL_MAIN_SEPARATOR_STR,
64+
expr.span,
65+
"taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`",
66+
"replace with",
67+
"std::path::MAIN_SEPARATOR_STR".to_owned(),
68+
Applicability::MachineApplicable,
69+
);
70+
}
71+
}
72+
}

clippy_utils/src/msrvs.rs

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ macro_rules! msrv_aliases {
1919

2020
// names may refer to stabilized feature flags or library items
2121
msrv_aliases! {
22+
1,68,0 { PATH_MAIN_SEPARATOR_STR }
2223
1,65,0 { LET_ELSE }
2324
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
2425
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY }

clippy_utils/src/paths.rs

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard
6767
pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"];
6868
pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
6969
pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
70+
pub const PATH_MAIN_SEPARATOR: [&str; 3] = ["std", "path", "MAIN_SEPARATOR"];
7071
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
7172
pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekable"];
7273
pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// run-rustfix
2+
3+
#![allow(unused)]
4+
#![warn(clippy::manual_main_separator_str)]
5+
6+
use std::path::MAIN_SEPARATOR;
7+
8+
fn main() {
9+
// Should lint
10+
let _: &str = std::path::MAIN_SEPARATOR_STR;
11+
12+
// Should not lint
13+
let _: &String = &MAIN_SEPARATOR.to_string();
14+
}

tests/ui/manual_main_separator_str.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// run-rustfix
2+
3+
#![allow(unused)]
4+
#![warn(clippy::manual_main_separator_str)]
5+
6+
use std::path::MAIN_SEPARATOR;
7+
8+
fn main() {
9+
// Should lint
10+
let _: &str = &MAIN_SEPARATOR.to_string();
11+
12+
// Should not lint
13+
let _: &String = &MAIN_SEPARATOR.to_string();
14+
}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`
2+
--> $DIR/manual_main_separator_str.rs:10:19
3+
|
4+
LL | let _: &str = &MAIN_SEPARATOR.to_string();
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::path::MAIN_SEPARATOR_STR`
6+
|
7+
= note: `-D clippy::manual-main-separator-str` implied by `-D warnings`
8+
9+
error: aborting due to previous error
10+

0 commit comments

Comments
 (0)