Skip to content

Commit 65a6a98

Browse files
committed
Add suspicious_command_arg_space lint.
1 parent 006a4cc commit 65a6a98

File tree

3 files changed

+70
-0
lines changed

3 files changed

+70
-0
lines changed

clippy_lints/src/methods/mod.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ mod skip_while_next;
8080
mod stable_sort_primitive;
8181
mod str_splitn;
8282
mod string_extend_chars;
83+
mod suspicious_command_arg_space;
8384
mod suspicious_map;
8485
mod suspicious_splitn;
8586
mod suspicious_to_owned;
@@ -3162,6 +3163,32 @@ declare_clippy_lint! {
31623163
"collecting an iterator when collect is not needed"
31633164
}
31643165

3166+
declare_clippy_lint! {
3167+
/// ### What it does
3168+
///
3169+
/// Checks for `Command::arg()` invocations that look like they
3170+
/// should be multiple arguments instead, such as `arg("-t ext2")`.
3171+
///
3172+
/// ### Why is this bad?
3173+
///
3174+
/// Arguments are not split by space. An argument like `arg("-t ext2")`
3175+
/// will be passed as a single argument to the command,
3176+
/// which is likely not what was intended.
3177+
///
3178+
/// ### Example
3179+
/// ```rust
3180+
/// std::process::Command::new("echo").arg("-n hello").spawn().unwrap();
3181+
/// ```
3182+
/// Use instead:
3183+
/// ```rust
3184+
/// std::process::Command::new("echo").args(["-n", "hello"]).spawn().unwrap();
3185+
/// ```
3186+
#[clippy::version = "1.67.0"]
3187+
pub SUSPICIOUS_COMMAND_ARG_SPACE,
3188+
suspicious,
3189+
"single command line argument that looks like it should be multiple arguments"
3190+
}
3191+
31653192
pub struct Methods {
31663193
avoid_breaking_exported_api: bool,
31673194
msrv: Msrv,
@@ -3496,6 +3523,9 @@ impl Methods {
34963523
unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
34973524
}
34983525
},
3526+
("arg", [arg]) => {
3527+
suspicious_command_arg_space::check(cx, recv, arg, span);
3528+
}
34993529
("as_deref" | "as_deref_mut", []) => {
35003530
needless_option_as_deref::check(cx, expr, recv, name);
35013531
},
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::paths;
3+
use clippy_utils::ty::match_type;
4+
use rustc_ast as ast;
5+
use rustc_errors::{Applicability, Diagnostic};
6+
use rustc_hir as hir;
7+
use rustc_lint::LateContext;
8+
use rustc_span::Span;
9+
10+
use super::SUSPICIOUS_COMMAND_ARG_SPACE;
11+
12+
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, span: Span) {
13+
let ty = cx.typeck_results().expr_ty(recv).peel_refs();
14+
15+
if match_type(cx, ty, &paths::STD_PROCESS_COMMAND)
16+
&& let hir::ExprKind::Lit(lit) = &arg.kind
17+
&& let ast::LitKind::Str(s, _) = &lit.node
18+
&& let Some((arg1, arg2)) = s.as_str().split_once(' ')
19+
&& arg1.starts_with('-')
20+
&& arg1.chars().all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
21+
{
22+
span_lint_and_then(
23+
cx,
24+
SUSPICIOUS_COMMAND_ARG_SPACE,
25+
arg.span,
26+
"single argument that looks like it should be multiple arguments",
27+
|diag: &mut Diagnostic| {
28+
diag.multipart_suggestion_verbose(
29+
"consider splitting the argument",
30+
vec![
31+
(span, "args".to_string()),
32+
(arg.span, format!("[{arg1:?}, {arg2:?}]")),
33+
],
34+
Applicability::MaybeIncorrect,
35+
);
36+
}
37+
);
38+
}
39+
}

clippy_utils/src/paths.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ pub const STD_FS_CREATE_DIR: [&str; 3] = ["std", "fs", "create_dir"];
115115
pub const STD_IO_SEEK: [&str; 3] = ["std", "io", "Seek"];
116116
pub const STD_IO_SEEK_FROM_CURRENT: [&str; 4] = ["std", "io", "SeekFrom", "Current"];
117117
pub const STD_IO_SEEKFROM_START: [&str; 4] = ["std", "io", "SeekFrom", "Start"];
118+
pub const STD_PROCESS_COMMAND: [&str; 3] = ["std", "process", "Command"];
118119
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
119120
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
120121
pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];

0 commit comments

Comments
 (0)