Auto merge of #10483 - samueltardieu:issue-10480, r=llogiq

New lint to detect `&std::path::MAIN_SEPARATOR.to_string()`

Fixes #10480

changelog: [`manual_main_separator_str`] new lint
This commit is contained in:
bors 2023-03-17 13:49:53 +00:00
commit d3c8442d4d
9 changed files with 184 additions and 0 deletions

View File

@ -4662,6 +4662,7 @@ Released 2018-09-13
[`manual_instant_elapsed`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_instant_elapsed
[`manual_is_ascii_check`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_is_ascii_check
[`manual_let_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_let_else
[`manual_main_separator_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_main_separator_str
[`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive

View File

@ -263,6 +263,7 @@
crate::manual_clamp::MANUAL_CLAMP_INFO,
crate::manual_is_ascii_check::MANUAL_IS_ASCII_CHECK_INFO,
crate::manual_let_else::MANUAL_LET_ELSE_INFO,
crate::manual_main_separator_str::MANUAL_MAIN_SEPARATOR_STR_INFO,
crate::manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE_INFO,
crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO,
crate::manual_retain::MANUAL_RETAIN_INFO,

View File

@ -180,6 +180,7 @@
mod manual_clamp;
mod manual_is_ascii_check;
mod manual_let_else;
mod manual_main_separator_str;
mod manual_non_exhaustive;
mod manual_rem_euclid;
mod manual_retain;
@ -936,6 +937,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(|| Box::new(redundant_async_block::RedundantAsyncBlock));
store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute));
store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(msrv())));
// add lints here, do not remove this comment, it's used in `new_lint`
}

View File

@ -0,0 +1,72 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::{is_trait_method, match_def_path, paths, peel_hir_expr_refs};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::*;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
/// Checks for references on `std::path::MAIN_SEPARATOR.to_string()` used
/// to build a `&str`.
///
/// ### Why is this bad?
/// There exists a `std::path::MAIN_SEPARATOR_STR` which does not require
/// an extra memory allocation.
///
/// ### Example
/// ```rust
/// let s: &str = &std::path::MAIN_SEPARATOR.to_string();
/// ```
/// Use instead:
/// ```rust
/// let s: &str = std::path::MAIN_SEPARATOR_STR;
/// ```
#[clippy::version = "1.70.0"]
pub MANUAL_MAIN_SEPARATOR_STR,
complexity,
"`&std::path::MAIN_SEPARATOR.to_string()` can be replaced by `std::path::MAIN_SEPARATOR_STR`"
}
pub struct ManualMainSeparatorStr {
msrv: Msrv,
}
impl ManualMainSeparatorStr {
#[must_use]
pub fn new(msrv: Msrv) -> Self {
Self { msrv }
}
}
impl_lint_pass!(ManualMainSeparatorStr => [MANUAL_MAIN_SEPARATOR_STR]);
impl LateLintPass<'_> for ManualMainSeparatorStr {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR) &&
let (target, _) = peel_hir_expr_refs(expr) &&
is_trait_method(cx, target, sym::ToString) &&
let ExprKind::MethodCall(path, receiver, &[], _) = target.kind &&
path.ident.name == sym::to_string &&
let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind &&
let Res::Def(DefKind::Const, receiver_def_id) = path.res &&
match_def_path(cx, receiver_def_id, &paths::PATH_MAIN_SEPARATOR) &&
let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind() &&
ty.is_str()
{
span_lint_and_sugg(
cx,
MANUAL_MAIN_SEPARATOR_STR,
expr.span,
"taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`",
"replace with",
"std::path::MAIN_SEPARATOR_STR".to_owned(),
Applicability::MachineApplicable,
);
}
}
}

View File

@ -19,6 +19,7 @@ macro_rules! msrv_aliases {
// names may refer to stabilized feature flags or library items
msrv_aliases! {
1,68,0 { PATH_MAIN_SEPARATOR_STR }
1,65,0 { LET_ELSE }
1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY }

View File

@ -67,6 +67,7 @@
pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"];
pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
pub const PATH_BUF_AS_PATH: [&str; 4] = ["std", "path", "PathBuf", "as_path"];
pub const PATH_MAIN_SEPARATOR: [&str; 3] = ["std", "path", "MAIN_SEPARATOR"];
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
pub const PEEKABLE: [&str; 5] = ["core", "iter", "adapters", "peekable", "Peekable"];
pub const PERMISSIONS: [&str; 3] = ["std", "fs", "Permissions"];

View File

@ -0,0 +1,39 @@
// run-rustfix
#![allow(unused)]
#![warn(clippy::manual_main_separator_str)]
use std::path::MAIN_SEPARATOR;
fn len(s: &str) -> usize {
s.len()
}
struct U<'a> {
f: &'a str,
g: &'a String,
}
struct V<T> {
f: T,
}
fn main() {
// Should lint
let _: &str = std::path::MAIN_SEPARATOR_STR;
let _ = len(std::path::MAIN_SEPARATOR_STR);
let _: Vec<u16> = std::path::MAIN_SEPARATOR_STR.encode_utf16().collect();
// Should lint for field `f` only
let _ = U {
f: std::path::MAIN_SEPARATOR_STR,
g: &MAIN_SEPARATOR.to_string(),
};
// Should not lint
let _: &String = &MAIN_SEPARATOR.to_string();
let _ = &MAIN_SEPARATOR.to_string();
let _ = V {
f: &MAIN_SEPARATOR.to_string(),
};
}

View File

@ -0,0 +1,39 @@
// run-rustfix
#![allow(unused)]
#![warn(clippy::manual_main_separator_str)]
use std::path::MAIN_SEPARATOR;
fn len(s: &str) -> usize {
s.len()
}
struct U<'a> {
f: &'a str,
g: &'a String,
}
struct V<T> {
f: T,
}
fn main() {
// Should lint
let _: &str = &MAIN_SEPARATOR.to_string();
let _ = len(&MAIN_SEPARATOR.to_string());
let _: Vec<u16> = MAIN_SEPARATOR.to_string().encode_utf16().collect();
// Should lint for field `f` only
let _ = U {
f: &MAIN_SEPARATOR.to_string(),
g: &MAIN_SEPARATOR.to_string(),
};
// Should not lint
let _: &String = &MAIN_SEPARATOR.to_string();
let _ = &MAIN_SEPARATOR.to_string();
let _ = V {
f: &MAIN_SEPARATOR.to_string(),
};
}

View File

@ -0,0 +1,28 @@
error: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`
--> $DIR/manual_main_separator_str.rs:23:19
|
LL | let _: &str = &MAIN_SEPARATOR.to_string();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::path::MAIN_SEPARATOR_STR`
|
= note: `-D clippy::manual-main-separator-str` implied by `-D warnings`
error: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`
--> $DIR/manual_main_separator_str.rs:24:17
|
LL | let _ = len(&MAIN_SEPARATOR.to_string());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::path::MAIN_SEPARATOR_STR`
error: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`
--> $DIR/manual_main_separator_str.rs:25:23
|
LL | let _: Vec<u16> = MAIN_SEPARATOR.to_string().encode_utf16().collect();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::path::MAIN_SEPARATOR_STR`
error: taking a reference on `std::path::MAIN_SEPARATOR` conversion to `String`
--> $DIR/manual_main_separator_str.rs:29:12
|
LL | f: &MAIN_SEPARATOR.to_string(),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::path::MAIN_SEPARATOR_STR`
error: aborting due to 4 previous errors