@@ -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;
@@ -3631,6 +3632,42 @@ declare_clippy_lint! {
36313632 "`as_str` used to call a method on `str` that is also available on `String`"
36323633}
36333634
3635+ declare_clippy_lint ! {
3636+ /// Checks for calls to `Path::join` that start with a path separator, like `\\` or `/`..
3637+ ///
3638+ /// ### Why is this bad?
3639+ /// `.join()` arguments starting with a separator (`/` or `\\`) can replace the entire path.
3640+ /// If this is intentional, prefer using `Path::new()` instead.
3641+ ///
3642+ /// See [`Path::join()`](https://doc.rust-lang.org/std/path/struct.Path.html#method.join)
3643+ ///
3644+ /// ### Example
3645+ /// ```rust
3646+ /// # use std::path::{Path, PathBuf};
3647+ /// let path = Path::new("/bin");
3648+ /// let joined_path = path.join("/sh");
3649+ /// assert_eq!(joined_path, PathBuf::from("/sh"));
3650+ /// ```
3651+ ///
3652+ /// Use instead;
3653+ /// ```rust
3654+ /// # use std::path::{Path, PathBuf};
3655+ /// let path = Path::new("/bin");
3656+ ///
3657+ /// // If this was unintentional, remove the leading separator
3658+ /// let joined_path = path.join("sh");
3659+ /// assert_eq!(joined_path, PathBuf::from("/bin/sh"));
3660+ ///
3661+ /// // If this was intentional, create a new path instead
3662+ /// let new = Path::new("/sh");
3663+ /// assert_eq!(new, PathBuf::from("/sh"));
3664+ /// ```
3665+ #[ clippy:: version = "1.74.0" ]
3666+ pub JOIN_ABSOLUTE_PATHS ,
3667+ correctness,
3668+ "arg to .join called on a `Path` contains leading separator"
3669+ }
3670+
36343671pub struct Methods {
36353672 avoid_breaking_exported_api : bool ,
36363673 msrv : Msrv ,
@@ -3776,6 +3813,7 @@ impl_lint_pass!(Methods => [
37763813 ITER_OUT_OF_BOUNDS ,
37773814 PATH_ENDS_WITH_EXT ,
37783815 REDUNDANT_AS_STR ,
3816+ JOIN_ABSOLUTE_PATHS ,
37793817] ) ;
37803818
37813819/// Extracts a method call name, args, and `Span` of the method name.
@@ -4139,6 +4177,8 @@ impl Methods {
41394177 ( "join" , [ join_arg] ) => {
41404178 if let Some ( ( "collect" , _, _, span, _) ) = method_call ( recv) {
41414179 unnecessary_join:: check ( cx, expr, recv, join_arg, span) ;
4180+ } else {
4181+ join_absolute_paths:: check ( cx, recv, join_arg) ;
41424182 }
41434183 } ,
41444184 ( "last" , [ ] ) => {
0 commit comments