Skip to content

Commit b61633e

Browse files
ofeegxFrednet
authored andcommitted
New lint clippy::join_absolute_paths
1 parent 9a4dd10 commit b61633e

File tree

6 files changed

+121
-0
lines changed

6 files changed

+121
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5129,6 +5129,7 @@ Released 2018-09-13
51295129
[`iter_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_with_drain
51305130
[`iter_without_into_iter`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_without_into_iter
51315131
[`iterator_step_by_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iterator_step_by_zero
5132+
[`join_absolute_paths`]: https://rust-lang.github.io/rust-clippy/master/index.html#join_absolute_paths
51325133
[`just_underscores_and_digits`]: https://rust-lang.github.io/rust-clippy/master/index.html#just_underscores_and_digits
51335134
[`large_const_arrays`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_const_arrays
51345135
[`large_digit_groups`]: https://rust-lang.github.io/rust-clippy/master/index.html#large_digit_groups

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
374374
crate::methods::ITER_SKIP_NEXT_INFO,
375375
crate::methods::ITER_SKIP_ZERO_INFO,
376376
crate::methods::ITER_WITH_DRAIN_INFO,
377+
crate::methods::JOIN_ABSOLUTE_PATHS_INFO,
377378
crate::methods::MANUAL_FILTER_MAP_INFO,
378379
crate::methods::MANUAL_FIND_MAP_INFO,
379380
crate::methods::MANUAL_NEXT_BACK_INFO,
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::ty::is_type_diagnostic_item;
3+
use rustc_ast::ast::LitKind;
4+
use rustc_hir::{Expr, ExprKind};
5+
use rustc_lint::LateContext;
6+
use rustc_span::symbol::sym::Path;
7+
8+
use super::JOIN_ABSOLUTE_PATHS;
9+
10+
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, join_arg: &'tcx Expr<'tcx>) {
11+
let ty = cx.typeck_results().expr_ty(expr).peel_refs();
12+
if is_type_diagnostic_item(cx, ty, Path)
13+
&& let ExprKind::Lit(spanned) = &join_arg.kind
14+
&& let LitKind::Str(symbol, _) = spanned.node
15+
&& (symbol.as_str().starts_with('/') || symbol.as_str().starts_with('\\'))
16+
{
17+
span_lint_and_then(
18+
cx,
19+
JOIN_ABSOLUTE_PATHS,
20+
join_arg.span,
21+
"argument to `Path::join` starts with a path separator",
22+
|diag| {
23+
diag
24+
.note("joining a path starting with separator will replace the path instead")
25+
.help("if this is unintentional, try removing the starting separator")
26+
.help("if this is intentional, try creating a new Path instead");
27+
},
28+
);
29+
}
30+
}

clippy_lints/src/methods/mod.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ mod iter_skip_next;
4949
mod iter_skip_zero;
5050
mod iter_with_drain;
5151
mod iterator_step_by_zero;
52+
mod join_absolute_paths;
5253
mod manual_next_back;
5354
mod manual_ok_or;
5455
mod manual_saturating_arithmetic;
@@ -3682,6 +3683,42 @@ declare_clippy_lint! {
36823683
"calling the `try_from` and `try_into` trait methods when `From`/`Into` is implemented"
36833684
}
36843685

3686+
declare_clippy_lint! {
3687+
/// Checks for calls to `Path::join` that start with a path separator, like `\\` or `/`..
3688+
///
3689+
/// ### Why is this bad?
3690+
/// `.join()` arguments starting with a separator (`/` or `\\`) can replace the entire path.
3691+
/// If this is intentional, prefer using `Path::new()` instead.
3692+
///
3693+
/// See [`Path::join()`](https://doc.rust-lang.org/std/path/struct.Path.html#method.join)
3694+
///
3695+
/// ### Example
3696+
/// ```rust
3697+
/// # use std::path::{Path, PathBuf};
3698+
/// let path = Path::new("/bin");
3699+
/// let joined_path = path.join("/sh");
3700+
/// assert_eq!(joined_path, PathBuf::from("/sh"));
3701+
/// ```
3702+
///
3703+
/// Use instead;
3704+
/// ```rust
3705+
/// # use std::path::{Path, PathBuf};
3706+
/// let path = Path::new("/bin");
3707+
///
3708+
/// // If this was unintentional, remove the leading separator
3709+
/// let joined_path = path.join("sh");
3710+
/// assert_eq!(joined_path, PathBuf::from("/bin/sh"));
3711+
///
3712+
/// // If this was intentional, create a new path instead
3713+
/// let new = Path::new("/sh");
3714+
/// assert_eq!(new, PathBuf::from("/sh"));
3715+
/// ```
3716+
#[clippy::version = "1.75.0"]
3717+
pub JOIN_ABSOLUTE_PATHS,
3718+
correctness,
3719+
"arg to .join called on a `Path` contains leading separator"
3720+
}
3721+
36853722
pub struct Methods {
36863723
avoid_breaking_exported_api: bool,
36873724
msrv: Msrv,
@@ -3829,6 +3866,7 @@ impl_lint_pass!(Methods => [
38293866
REDUNDANT_AS_STR,
38303867
WAKER_CLONE_WAKE,
38313868
UNNECESSARY_FALLIBLE_CONVERSIONS,
3869+
JOIN_ABSOLUTE_PATHS,
38323870
]);
38333871

38343872
/// Extracts a method call name, args, and `Span` of the method name.
@@ -4231,6 +4269,8 @@ impl Methods {
42314269
("join", [join_arg]) => {
42324270
if let Some(("collect", _, _, span, _)) = method_call(recv) {
42334271
unnecessary_join::check(cx, expr, recv, join_arg, span);
4272+
} else {
4273+
join_absolute_paths::check(cx, recv, join_arg);
42344274
}
42354275
},
42364276
("last", []) => {

tests/ui/join_absolute_paths.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#![allow(unused)]
2+
#![warn(clippy::join_absolute_paths)]
3+
4+
use std::path::{Path, PathBuf};
5+
6+
fn main() {
7+
// should be linted
8+
let path = Path::new("/bin");
9+
path.join("/sh");
10+
11+
// should be linted
12+
let path = Path::new("C:\\Users");
13+
path.join("\\user");
14+
15+
// should not be linted
16+
let path: &[&str] = &["/bin"];
17+
path.join("/sh");
18+
19+
// should not be linted
20+
let path = Path::new("/bin");
21+
path.join("sh");
22+
23+
// should be linted
24+
let path = PathBuf::from("/bin");
25+
path.join("/sh");
26+
}

tests/ui/join_absolute_paths.stderr

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error: argument to `Path::join` starts with a path separator
2+
--> $DIR/join_absolute_paths.rs:9:15
3+
|
4+
LL | path.join("/sh");
5+
| ^^^^^
6+
|
7+
= note: joining a path starting with separator will replace the path instead
8+
= help: if this is unintentional, try removing the starting separator
9+
= help: if this is intentional, try creating a new Path instead
10+
= note: `-D clippy::join-absolute-paths` implied by `-D warnings`
11+
12+
error: argument to `Path::join` starts with a path separator
13+
--> $DIR/join_absolute_paths.rs:13:15
14+
|
15+
LL | path.join("\\user");
16+
| ^^^^^^^^
17+
|
18+
= note: joining a path starting with separator will replace the path instead
19+
= help: if this is unintentional, try removing the starting separator
20+
= help: if this is intentional, try creating a new Path instead
21+
22+
error: aborting due to 2 previous errors
23+

0 commit comments

Comments
 (0)