@@ -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;
@@ -3563,6 +3564,43 @@ declare_clippy_lint! {
35633564 "calls to `.take()` or `.skip()` that are out of bounds"
35643565}
35653566
3567+ declare_clippy_lint ! {
3568+ /// ### What it does
3569+ /// Checks for calls to `Path::join` that start with a path separator, like `\\` or `/`..
3570+ ///
3571+ /// ### Why is this bad?
3572+ /// `.join()` arguments starting with a separator (`/` or `\\`) can replace the entire path.
3573+ /// If this is intentional, prefer using `Path::new()` instead.
3574+ ///
3575+ /// See [`Path::join()`](https://doc.rust-lang.org/std/path/struct.Path.html#method.join)
3576+ ///
3577+ /// ### Example
3578+ /// ```rust
3579+ /// # use std::path::{Path, PathBuf};
3580+ /// let path = Path::new("/bin");
3581+ /// let joined_path = path.join("/sh");
3582+ /// assert_eq!(joined_path, PathBuf::from("/sh"));
3583+ /// ```
3584+ ///
3585+ /// Use instead;
3586+ /// ```rust
3587+ /// # use std::path::{Path, PathBuf};
3588+ /// let path = Path::new("/bin");
3589+ ///
3590+ /// // If this was unintentional, remove the leading separator
3591+ /// let joined_path = path.join("sh");
3592+ /// assert_eq!(joined_path, PathBuf::from("/bin/sh"));
3593+ ///
3594+ /// // If this was intentional, create a new path instead
3595+ /// let new = Path::new("/sh");
3596+ /// assert_eq!(new, PathBuf::from("/sh"));
3597+ /// ```
3598+ #[ clippy:: version = "1.74.0" ]
3599+ pub JOIN_ABSOLUTE_PATHS ,
3600+ correctness,
3601+ "arg to .join called on a `Path` contains leading separator"
3602+ }
3603+
35663604pub struct Methods {
35673605 avoid_breaking_exported_api : bool ,
35683606 msrv : Msrv ,
@@ -3703,6 +3741,7 @@ impl_lint_pass!(Methods => [
37033741 FILTER_MAP_BOOL_THEN ,
37043742 READONLY_WRITE_LOCK ,
37053743 ITER_OUT_OF_BOUNDS ,
3744+ JOIN_ABSOLUTE_PATHS ,
37063745] ) ;
37073746
37083747/// Extracts a method call name, args, and `Span` of the method name.
@@ -4062,6 +4101,8 @@ impl Methods {
40624101 ( "join" , [ join_arg] ) => {
40634102 if let Some ( ( "collect" , _, _, span, _) ) = method_call ( recv) {
40644103 unnecessary_join:: check ( cx, expr, recv, join_arg, span) ;
4104+ } else {
4105+ join_absolute_paths:: check ( cx, recv, join_arg) ;
40654106 }
40664107 } ,
40674108 ( "last" , [ ] ) => {
0 commit comments