Skip to content

Commit c6e43b1

Browse files
committed
Auto merge of #3954 - andrehjr:add-lint-path-buf-overwrite, r=flip1995
Add Lint PathBufPushOverwrite Closes #3923 This is a very simple Lint that checks if push is being called with a Root Path. Because that can make it overwrite the previous path. I used std::path::Path to check if it's root, in order to keep it working across windows/linux environments instead of checking for '/'. Is that alright? On the `if_chain!` block, Is there a way to make it short while getting the value of the first argument? I got the example from other lints. Note that this is first Lint, I hope I got everything covered 🚀
2 parents 6feed27 + 7e9cb5b commit c6e43b1

7 files changed

+101
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1015,6 +1015,7 @@ All notable changes to this project will be documented in this file.
10151015
[`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
10161016
[`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
10171017
[`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
1018+
[`path_buf_push_overwrite`]: https://rust-lang.github.io/rust-clippy/master/index.html#path_buf_push_overwrite
10181019
[`possible_missing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#possible_missing_comma
10191020
[`precedence`]: https://rust-lang.github.io/rust-clippy/master/index.html#precedence
10201021
[`print_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#print_literal

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
99

10-
[There are 298 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
10+
[There are 299 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
1111

1212
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1313

clippy_lints/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ pub mod open_options;
236236
pub mod overflow_check_conditional;
237237
pub mod panic_unimplemented;
238238
pub mod partialeq_ne_impl;
239+
pub mod path_buf_push_overwrite;
239240
pub mod precedence;
240241
pub mod ptr;
241242
pub mod ptr_offset_with_cast;
@@ -572,6 +573,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
572573
reg.register_late_lint_pass(box assertions_on_constants::AssertionsOnConstants);
573574
reg.register_late_lint_pass(box missing_const_for_fn::MissingConstForFn);
574575
reg.register_late_lint_pass(box transmuting_null::TransmutingNull);
576+
reg.register_late_lint_pass(box path_buf_push_overwrite::PathBufPushOverwrite);
575577

576578
reg.register_lint_group("clippy::restriction", Some("clippy_restriction"), vec![
577579
arithmetic::FLOAT_ARITHMETIC,
@@ -805,6 +807,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
805807
overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
806808
panic_unimplemented::PANIC_PARAMS,
807809
partialeq_ne_impl::PARTIALEQ_NE_IMPL,
810+
path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
808811
precedence::PRECEDENCE,
809812
ptr::CMP_NULL,
810813
ptr::MUT_FROM_REF,
@@ -1072,6 +1075,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
10721075
non_copy_const::BORROW_INTERIOR_MUTABLE_CONST,
10731076
non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST,
10741077
open_options::NONSENSICAL_OPEN_OPTIONS,
1078+
path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
10751079
ptr::MUT_FROM_REF,
10761080
ranges::ITERATOR_STEP_BY_ZERO,
10771081
regex::INVALID_REGEX,
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
use crate::utils::{match_type, paths, span_lint_and_sugg, walk_ptrs_ty};
2+
use if_chain::if_chain;
3+
use rustc::hir::*;
4+
use rustc::lint::{LateContext, LateLintPass, LintArray, LintPass};
5+
use rustc::{declare_lint_pass, declare_tool_lint};
6+
use rustc_errors::Applicability;
7+
use std::path::{Component, Path};
8+
use syntax::ast::LitKind;
9+
10+
declare_clippy_lint! {
11+
/// **What it does:*** Checks for [push](https://doc.rust-lang.org/std/path/struct.PathBuf.html#method.push)
12+
/// calls on `PathBuf` that can cause overwrites.
13+
///
14+
/// **Why is this bad?** Calling `push` with a root path at the start can overwrite the
15+
/// previous defined path.
16+
///
17+
/// **Known problems:** None.
18+
///
19+
/// **Example:**
20+
/// ```rust
21+
/// use std::path::PathBuf;
22+
///
23+
/// let mut x = PathBuf::from("/foo");
24+
/// x.push("/bar");
25+
/// assert_eq!(x, PathBuf::from("/bar"));
26+
/// ```
27+
/// Could be written:
28+
///
29+
/// ```rust
30+
/// use std::path::PathBuf;
31+
///
32+
/// let mut x = PathBuf::from("/foo");
33+
/// x.push("bar");
34+
/// assert_eq!(x, PathBuf::from("/foo/bar"));
35+
/// ```
36+
pub PATH_BUF_PUSH_OVERWRITE,
37+
correctness,
38+
"calling `push` with file system root on `PathBuf` can overwrite it"
39+
}
40+
41+
declare_lint_pass!(PathBufPushOverwrite => [PATH_BUF_PUSH_OVERWRITE]);
42+
43+
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PathBufPushOverwrite {
44+
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
45+
if_chain! {
46+
if let ExprKind::MethodCall(ref path, _, ref args) = expr.node;
47+
if path.ident.name == "push";
48+
if args.len() == 2;
49+
if match_type(cx, walk_ptrs_ty(cx.tables.expr_ty(&args[0])), &paths::PATH_BUF);
50+
if let Some(get_index_arg) = args.get(1);
51+
if let ExprKind::Lit(ref lit) = get_index_arg.node;
52+
if let LitKind::Str(ref path_lit, _) = lit.node;
53+
if let pushed_path = Path::new(&path_lit.as_str());
54+
if let Some(pushed_path_lit) = pushed_path.to_str();
55+
if pushed_path.has_root();
56+
if let Some(root) = pushed_path.components().next();
57+
if root == Component::RootDir;
58+
then {
59+
span_lint_and_sugg(
60+
cx,
61+
PATH_BUF_PUSH_OVERWRITE,
62+
lit.span,
63+
"Calling `push` with '/' or '\\' (file system root) will overwrite the previous path definition",
64+
"try",
65+
format!("\"{}\"", pushed_path_lit.trim_start_matches(|c| c == '/' || c == '\\')),
66+
Applicability::MachineApplicable,
67+
);
68+
}
69+
}
70+
}
71+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// run-rustfix
2+
use std::path::PathBuf;
3+
4+
fn main() {
5+
let mut x = PathBuf::from("/foo");
6+
x.push("bar");
7+
}

tests/ui/path_buf_push_overwrite.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// run-rustfix
2+
use std::path::PathBuf;
3+
4+
fn main() {
5+
let mut x = PathBuf::from("/foo");
6+
x.push("/bar");
7+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: Calling `push` with '/' or '/' (file system root) will overwrite the previous path definition
2+
--> $DIR/path_buf_push_overwrite.rs:6:12
3+
|
4+
LL | x.push("/bar");
5+
| ^^^^^^ help: try: `"bar"`
6+
|
7+
= note: #[deny(clippy::path_buf_push_overwrite)] on by default
8+
9+
error: aborting due to previous error
10+

0 commit comments

Comments
 (0)