Skip to content

Commit

Permalink
New lint clippy::join_absolute_paths
Browse files Browse the repository at this point in the history
  • Loading branch information
ofeeg authored and xFrednet committed Sep 25, 2023
1 parent 78ddc8d commit 38ac7be
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5037,6 +5037,7 @@ Released 2018-09-13
[`iter_skip_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_zero
[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
[`join_absolute_paths`]: https://rust-lang.github.io/rust-clippy/master/index.html#join_absolute_paths
[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups
Expand Down
1 change: 1 addition & 0 deletions clippy_lints/src/declared_lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::methods::ITER_SKIP_NEXT_INFO,
crate::methods::ITER_SKIP_ZERO_INFO,
crate::methods::ITER_WITH_DRAIN_INFO,
crate::methods::JOIN_ABSOLUTE_PATHS_INFO,
crate::methods::MANUAL_FILTER_MAP_INFO,
crate::methods::MANUAL_FIND_MAP_INFO,
crate::methods::MANUAL_NEXT_BACK_INFO,
Expand Down
30 changes: 30 additions & 0 deletions clippy_lints/src/methods/join_absolute_paths.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_ast::ast::LitKind;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_span::symbol::sym::Path;

use super::JOIN_ABSOLUTE_PATHS;

pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, join_arg: &'tcx Expr<'tcx>) {
let ty = cx.typeck_results().expr_ty(expr).peel_refs();
if is_type_diagnostic_item(cx, ty, Path)
&& let ExprKind::Lit(spanned) = &join_arg.kind
&& let LitKind::Str(symbol, _) = spanned.node
&& (symbol.as_str().starts_with('/') || symbol.as_str().starts_with('\\'))
{
span_lint_and_then(
cx,
JOIN_ABSOLUTE_PATHS,
join_arg.span,
"argument to `Path::join` starts with a path separator",
|diag| {
diag
.note("joining a path starting with separator will replace the path instead")
.help("if this is unintentional, try removing the starting separator")
.help("if this is intentional, try creating a new Path instead");
},
);
}
}
40 changes: 40 additions & 0 deletions clippy_lints/src/methods/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ mod iter_skip_next;
mod iter_skip_zero;
mod iter_with_drain;
mod iterator_step_by_zero;
mod join_absolute_paths;
mod manual_next_back;
mod manual_ok_or;
mod manual_saturating_arithmetic;
Expand Down Expand Up @@ -3631,6 +3632,42 @@ declare_clippy_lint! {
"`as_str` used to call a method on `str` that is also available on `String`"
}

declare_clippy_lint! {
/// Checks for calls to `Path::join` that start with a path separator, like `\\` or `/`..
///
/// ### Why is this bad?
/// `.join()` arguments starting with a separator (`/` or `\\`) can replace the entire path.
/// If this is intentional, prefer using `Path::new()` instead.
///
/// See [`Path::join()`](https://doc.rust-lang.org/std/path/struct.Path.html#method.join)
///
/// ### Example
/// ```rust
/// # use std::path::{Path, PathBuf};
/// let path = Path::new("/bin");
/// let joined_path = path.join("/sh");
/// assert_eq!(joined_path, PathBuf::from("/sh"));
/// ```
///
/// Use instead;
/// ```rust
/// # use std::path::{Path, PathBuf};
/// let path = Path::new("/bin");
///
/// // If this was unintentional, remove the leading separator
/// let joined_path = path.join("sh");
/// assert_eq!(joined_path, PathBuf::from("/bin/sh"));
///
/// // If this was intentional, create a new path instead
/// let new = Path::new("/sh");
/// assert_eq!(new, PathBuf::from("/sh"));
/// ```
#[clippy::version = "1.74.0"]
pub JOIN_ABSOLUTE_PATHS,
correctness,
"arg to .join called on a `Path` contains leading separator"
}

pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
Expand Down Expand Up @@ -3776,6 +3813,7 @@ impl_lint_pass!(Methods => [
ITER_OUT_OF_BOUNDS,
PATH_ENDS_WITH_EXT,
REDUNDANT_AS_STR,
JOIN_ABSOLUTE_PATHS,
]);

/// Extracts a method call name, args, and `Span` of the method name.
Expand Down Expand Up @@ -4139,6 +4177,8 @@ impl Methods {
("join", [join_arg]) => {
if let Some(("collect", _, _, span, _)) = method_call(recv) {
unnecessary_join::check(cx, expr, recv, join_arg, span);
} else {
join_absolute_paths::check(cx, recv, join_arg);
}
},
("last", []) => {
Expand Down
26 changes: 26 additions & 0 deletions tests/ui/join_absolute_paths.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#![allow(unused)]
#![warn(clippy::join_absolute_paths)]

use std::path::{Path, PathBuf};

fn main() {
// should be linted
let path = Path::new("/bin");
path.join("/sh");

// should be linted
let path = Path::new("C:\\Users");
path.join("\\user");

// should not be linted
let path: &[&str] = &["/bin"];
path.join("/sh");

// should not be linted
let path = Path::new("/bin");
path.join("sh");

// should be linted
let path = PathBuf::from("/bin");
path.join("/sh");
}
23 changes: 23 additions & 0 deletions tests/ui/join_absolute_paths.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
error: argument to `Path::join` starts with a path separator
--> $DIR/join_absolute_paths.rs:9:15
|
LL | path.join("/sh");
| ^^^^^
|
= note: joining a path starting with separator will replace the path instead
= help: if this is unintentional, try removing the starting separator
= help: if this is intentional, try creating a new Path instead
= note: `-D clippy::join-absolute-paths` implied by `-D warnings`

error: argument to `Path::join` starts with a path separator
--> $DIR/join_absolute_paths.rs:13:15
|
LL | path.join("\\user");
| ^^^^^^^^
|
= note: joining a path starting with separator will replace the path instead
= help: if this is unintentional, try removing the starting separator
= help: if this is intentional, try creating a new Path instead

error: aborting due to 2 previous errors

0 comments on commit 38ac7be

Please sign in to comment.