diff --git a/CHANGELOG.md b/CHANGELOG.md
index c090a844858..47a503510c1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index 25f15973490..2331e857b1f 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -263,6 +263,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     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,
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index 4bb0e2ec96c..100cba45679 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -180,6 +180,7 @@ mod manual_bits;
 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`
 }
 
diff --git a/clippy_lints/src/manual_main_separator_str.rs b/clippy_lints/src/manual_main_separator_str.rs
new file mode 100644
index 00000000000..a6d318f952c
--- /dev/null
+++ b/clippy_lints/src/manual_main_separator_str.rs
@@ -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,
+                );
+            }
+    }
+}
diff --git a/clippy_utils/src/msrvs.rs b/clippy_utils/src/msrvs.rs
index dbf9f3b621d..e05de2dc99c 100644
--- a/clippy_utils/src/msrvs.rs
+++ b/clippy_utils/src/msrvs.rs
@@ -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 }
diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs
index 4aae0f7284e..c919575bfe9 100644
--- a/clippy_utils/src/paths.rs
+++ b/clippy_utils/src/paths.rs
@@ -67,6 +67,7 @@ pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard
 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"];
diff --git a/tests/ui/manual_main_separator_str.fixed b/tests/ui/manual_main_separator_str.fixed
new file mode 100644
index 00000000000..50f46d6b355
--- /dev/null
+++ b/tests/ui/manual_main_separator_str.fixed
@@ -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(),
+    };
+}
diff --git a/tests/ui/manual_main_separator_str.rs b/tests/ui/manual_main_separator_str.rs
new file mode 100644
index 00000000000..2dbb9e66151
--- /dev/null
+++ b/tests/ui/manual_main_separator_str.rs
@@ -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(),
+    };
+}
diff --git a/tests/ui/manual_main_separator_str.stderr b/tests/ui/manual_main_separator_str.stderr
new file mode 100644
index 00000000000..e6cefde66a7
--- /dev/null
+++ b/tests/ui/manual_main_separator_str.stderr
@@ -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
+