Skip to content

Commit 6d4e682

Browse files
committed
Add lint PathBufPushOverwrite
1 parent 95e537b commit 6d4e682

File tree

7 files changed

+102
-1
lines changed

7 files changed

+102
-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: 5 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,8 @@ 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 transmuting_null::Pass);
577+
reg.register_late_lint_pass(box path_buf_push_overwrite::PathBufPushOverwrite);
575578

576579
reg.register_lint_group("clippy::restriction", Some("clippy_restriction"), vec![
577580
arithmetic::FLOAT_ARITHMETIC,
@@ -805,6 +808,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
805808
overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
806809
panic_unimplemented::PANIC_PARAMS,
807810
partialeq_ne_impl::PARTIALEQ_NE_IMPL,
811+
path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
808812
precedence::PRECEDENCE,
809813
ptr::CMP_NULL,
810814
ptr::MUT_FROM_REF,
@@ -1072,6 +1076,7 @@ pub fn register_plugins(reg: &mut rustc_plugin::Registry<'_>, conf: &Conf) {
10721076
non_copy_const::BORROW_INTERIOR_MUTABLE_CONST,
10731077
non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST,
10741078
open_options::NONSENSICAL_OPEN_OPTIONS,
1079+
path_buf_push_overwrite::PATH_BUF_PUSH_OVERWRITE,
10751080
ptr::MUT_FROM_REF,
10761081
ranges::ITERATOR_STEP_BY_ZERO,
10771082
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)