@@ -49,6 +49,7 @@ mod iter_skip_next;
4949mod iter_skip_zero;
5050mod iter_with_drain;
5151mod iterator_step_by_zero;
52+ mod join_absolute_paths;
5253mod manual_next_back;
5354mod manual_ok_or;
5455mod manual_saturating_arithmetic;
@@ -3684,6 +3685,46 @@ declare_clippy_lint! {
36843685 "calling the `try_from` and `try_into` trait methods when `From`/`Into` is implemented"
36853686}
36863687
3688+ declare_clippy_lint ! {
3689+ /// ### What it does
3690+ /// Checks for calls to `Path::join` that start with a path separator (`\\` or `/`).
3691+ ///
3692+ /// ### Why is this bad?
3693+ /// If the argument to `Path::join` starts with a separator, it will overwrite
3694+ /// the original path. If this is intentional, prefer using `Path::new` instead.
3695+ ///
3696+ /// Note the behavior is platform dependent. A leading `\\` will be accepted
3697+ /// on unix systems as part of the file name
3698+ ///
3699+ /// See [`Path::join`](https://doc.rust-lang.org/std/path/struct.Path.html#method.join)
3700+ ///
3701+ /// ### Example
3702+ /// ```rust
3703+ /// # use std::path::{Path, PathBuf};
3704+ /// let path = Path::new("/bin");
3705+ /// let joined_path = path.join("/sh");
3706+ /// assert_eq!(joined_path, PathBuf::from("/sh"));
3707+ /// ```
3708+ ///
3709+ /// Use instead;
3710+ /// ```rust
3711+ /// # use std::path::{Path, PathBuf};
3712+ /// let path = Path::new("/bin");
3713+ ///
3714+ /// // If this was unintentional, remove the leading separator
3715+ /// let joined_path = path.join("sh");
3716+ /// assert_eq!(joined_path, PathBuf::from("/bin/sh"));
3717+ ///
3718+ /// // If this was intentional, create a new path instead
3719+ /// let new = Path::new("/sh");
3720+ /// assert_eq!(new, PathBuf::from("/sh"));
3721+ /// ```
3722+ #[ clippy:: version = "1.76.0" ]
3723+ pub JOIN_ABSOLUTE_PATHS ,
3724+ suspicious,
3725+ "calls to `Path::join` which will overwrite the original path"
3726+ }
3727+
36873728pub struct Methods {
36883729 avoid_breaking_exported_api : bool ,
36893730 msrv : Msrv ,
@@ -3833,6 +3874,7 @@ impl_lint_pass!(Methods => [
38333874 REDUNDANT_AS_STR ,
38343875 WAKER_CLONE_WAKE ,
38353876 UNNECESSARY_FALLIBLE_CONVERSIONS ,
3877+ JOIN_ABSOLUTE_PATHS ,
38363878] ) ;
38373879
38383880/// Extracts a method call name, args, and `Span` of the method name.
@@ -4235,6 +4277,8 @@ impl Methods {
42354277 ( "join" , [ join_arg] ) => {
42364278 if let Some ( ( "collect" , _, _, span, _) ) = method_call ( recv) {
42374279 unnecessary_join:: check ( cx, expr, recv, join_arg, span) ;
4280+ } else {
4281+ join_absolute_paths:: check ( cx, recv, join_arg, expr. span ) ;
42384282 }
42394283 } ,
42404284 ( "last" , [ ] ) => {
0 commit comments