Skip to content

Commit ffabdab

Browse files
committed
New lint to detect &std::path::MAIN_SEPARATOR.to_string()
1 parent 5afa93b commit ffabdab

9 files changed

+184
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -4662,6 +4662,7 @@ Released 2018-09-13
46624662
[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
46634663
[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
46644664
[`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else
4665+
[`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str
46654666
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
46664667
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
46674668
[`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
@@ -263,6 +263,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
263263
crate::manual_clamp::MANUAL_CLAMP_INFO,
264264
crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
265265
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
266+
crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO,
266267
crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
267268
crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO,
268269
crate::manual_retain::MANUAL_RETAIN_INFO,

clippy_lints/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ mod manual_bits;
180180
mod manual_clamp;
181181
mod manual_is_ascii_check;
182182
mod manual_let_else;
183+
mod manual_main_separator_str;
183184
mod manual_non_exhaustive;
184185
mod manual_rem_euclid;
185186
mod manual_retain;
@@ -936,6 +937,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
936937
store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock));
937938
store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
938939
store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute));
940+
store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(msrv())));
939941
// add lints here, do not remove this comment, it's used in `new_lint`
940942
}
941943

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, peel_hir_expr_refs};
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 LateLintPass<'_> for ManualMainSeparatorStr {
49+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
50+
if self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR) &&
51+
let (target, _) = peel_hir_expr_refs(expr) &&
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"];
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// run-rustfix
2+
3+
#![allow(unused)]
4+
#![warn(clippy::manual_main_separator_str)]
5+
6+
use std::path::MAIN_SEPARATOR;
7+
8+
fn len(s: &str) -> usize {
9+
s.len()
10+
}
11+
12+
struct U<'a> {
13+
f: &'a str,
14+
g: &'a String,
15+
}
16+
17+
struct V<T> {
18+
f: T,
19+
}
20+
21+
fn main() {
22+
// Should lint
23+
let _: &str = std::path::MAIN_SEPARATOR_STR;
24+
let _ = len(std::path::MAIN_SEPARATOR_STR);
25+
let _: Vec<u16> = std::path::MAIN_SEPARATOR_STR.encode_utf16().collect();
26+
27+
// Should lint for field `f` only
28+
let _ = U {
29+
f: std::path::MAIN_SEPARATOR_STR,
30+
g: &MAIN_SEPARATOR.to_string(),
31+
};
32+
33+
// Should not lint
34+
let _: &String = &MAIN_SEPARATOR.to_string();
35+
let _ = &MAIN_SEPARATOR.to_string();
36+
let _ = V {
37+
f: &MAIN_SEPARATOR.to_string(),
38+
};
39+
}

tests/ui/manual_main_separator_str.rs

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// run-rustfix
2+
3+
#![allow(unused)]
4+
#![warn(clippy::manual_main_separator_str)]
5+
6+
use std::path::MAIN_SEPARATOR;
7+
8+
fn len(s: &str) -> usize {
9+
s.len()
10+
}
11+
12+
struct U<'a> {
13+
f: &'a str,
14+
g: &'a String,
15+
}
16+
17+
struct V<T> {
18+
f: T,
19+
}
20+
21+
fn main() {
22+
// Should lint
23+
let _: &str = &MAIN_SEPARATOR.to_string();
24+
let _ = len(&MAIN_SEPARATOR.to_string());
25+
let _: Vec<u16> = MAIN_SEPARATOR.to_string().encode_utf16().collect();
26+
27+
// Should lint for field `f` only
28+
let _ = U {
29+
f: &MAIN_SEPARATOR.to_string(),
30+
g: &MAIN_SEPARATOR.to_string(),
31+
};
32+
33+
// Should not lint
34+
let _: &String = &MAIN_SEPARATOR.to_string();
35+
let _ = &MAIN_SEPARATOR.to_string();
36+
let _ = V {
37+
f: &MAIN_SEPARATOR.to_string(),
38+
};
39+
}
+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
error: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`
2+
--> $DIR/manual_main_separator_str.rs:23: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: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`
10+
--> $DIR/manual_main_separator_str.rs:24:17
11+
|
12+
LL | let _ = len(&MAIN_SEPARATOR.to_string());
13+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::path::MAIN_SEPARATOR_STR`
14+
15+
error: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`
16+
--> $DIR/manual_main_separator_str.rs:25:23
17+
|
18+
LL | let _: Vec<u16> = MAIN_SEPARATOR.to_string().encode_utf16().collect();
19+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::path::MAIN_SEPARATOR_STR`
20+
21+
error: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`
22+
--> $DIR/manual_main_separator_str.rs:29:12
23+
|
24+
LL | f: &MAIN_SEPARATOR.to_string(),
25+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::path::MAIN_SEPARATOR_STR`
26+
27+
error: aborting due to 4 previous errors
28+

0 commit comments

Comments
 (0)