Skip to content

Commit 1b145d4

Browse files
committed
added suggestion
1 parent 6b0796d commit 1b145d4

File tree

2 files changed

+53
-42
lines changed

2 files changed

+53
-42
lines changed

clippy_lints/src/path_from_format.rs

Lines changed: 52 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
use clippy_utils::diagnostics::span_lint_and_help;
2-
use clippy_utils::match_qpath;
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::ty::is_type_diagnostic_item;
3+
use clippy_utils::source::snippet;
34
use rustc_hir::{Expr, ExprKind};
45
use rustc_lint::{LateContext, LateLintPass};
56
use rustc_session::{declare_lint_pass, declare_tool_lint};
67
use rustc_span::sym;
8+
use rustc_errors::Applicability;
79

810
declare_clippy_lint! {
911
/// ### What it does
@@ -31,52 +33,62 @@ declare_lint_pass!(PathFromFormat => [PATH_FROM_FORMAT]);
3133
impl<'tcx> LateLintPass<'tcx> for PathFromFormat {
3234
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
3335
if_chain! {
34-
if let ExprKind::Call(func, args) = expr.kind;
35-
if let ExprKind::Path(ref qpath) = func.kind;
36-
if match_qpath(qpath, &["PathBuf", "from"]);
37-
// if args.len() == 1;
36+
if let ExprKind::Call(_, ref args) = expr.kind;
37+
if let ty = cx.typeck_results().expr_ty(expr);
38+
if is_type_diagnostic_item(cx, ty, sym::PathBuf);
3839
if let Some(macro_def_id) = args[0].span.ctxt().outer_expn_data().macro_def_id;
3940
if cx.tcx.get_diagnostic_name(macro_def_id) == Some(sym::format_macro);
40-
// if let ExprKind::Block(block, None) = args[0].kind;
41-
// if block.stmts.len() == 1;
42-
// if let StmtKind::Local(local) = block.stmts[0].kind;
43-
// if let Some(init) = local.init;
44-
// if let ExprKind::Call(func1, args1) = init.kind;
45-
// if let ExprKind::Path(ref qpath1) = func1.kind;
46-
// if match_qpath(qpath1, &["$crate", "fmt", "format"]);
47-
// if args1.len() == 1;
48-
// if let ExprKind::Call(func2, args2) = args1[0].kind;
49-
// if let ExprKind::Path(ref qpath2) = func2.kind;
50-
// if match_qpath(qpath2, &["$crate", "fmt", "Arguments", "new_v1"]);
51-
// if args2.len() == 2;
52-
// if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args2[0].kind;
53-
// if let ExprKind::Array(elements) = inner.kind;
54-
// if elements.len() == 2;
55-
// if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner1) = args2[1].kind;
56-
// if let ExprKind::Array(elements1) = inner1.kind;
57-
// if elements1.len() == 1;
58-
// if let ExprKind::Call(func3, args3) = elements1[0].kind;
59-
// if let ExprKind::Path(ref qpath3) = func3.kind;
60-
// if match_qpath(qpath3, &["$crate", "fmt", "ArgumentV1", "new_display"]);
61-
// if args3.len() == 1;
62-
// if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args3[0].kind;
63-
// if let ExprKind::Path(ref qpath4) = inner2.kind;
64-
// if match_qpath(qpath4, &["base_path"]);
65-
// if let PatKind::Binding(BindingAnnotation::Unannotated, _, name, None) = local.pat.kind;
66-
// if name.as_str() == "res";
67-
// if let Some(trailing_expr) = block.expr;
68-
// if let ExprKind::Path(ref qpath5) = trailing_expr.kind;
69-
// if match_qpath(qpath5, &["res"]);
7041
then {
71-
span_lint_and_help(
42+
let full_expr = snippet(cx, expr.span, "error").to_string();
43+
let split_expr: Vec<&str> = full_expr.split('!').collect();
44+
let args_to_macro = split_expr[1];
45+
let replaced = args_to_macro.replace('(', "").replace(')', "");
46+
let unformatted: Vec<&str> = replaced.split(",").collect();
47+
let mut push_targets: Vec<String> = Vec::new();
48+
let mut temp_string = String::new();
49+
for c in unformatted[0].chars() {
50+
if c == '/' || c == '\\' {
51+
push_targets.push(temp_string.clone());
52+
temp_string = String::new();
53+
}
54+
else if c == '}' {
55+
temp_string.push_str(&unformatted[1].replace(' ', ""));
56+
}
57+
else if c != '{' && c != '"' {
58+
temp_string.push(c);
59+
}
60+
}
61+
if !temp_string.is_empty() {
62+
push_targets.push(temp_string.clone());
63+
temp_string = String::new();
64+
}
65+
for target in push_targets {
66+
let target_processed =
67+
if target != unformatted[1].replace(' ', "") {
68+
let mut s = String::from("\"");
69+
s.push_str(&target);
70+
s.push('"');
71+
s
72+
}
73+
else {
74+
target
75+
};
76+
if temp_string.is_empty() {
77+
temp_string.push_str(&format!("Path::new({})", target_processed));
78+
}
79+
else {
80+
temp_string.push_str(&format!(".join({})", target_processed));
81+
}
82+
}
83+
span_lint_and_sugg(
7284
cx,
7385
PATH_FROM_FORMAT,
7486
expr.span,
7587
"`format!(..)` used to form `PathBuf`",
76-
None,
77-
"consider using `.join()` or `.push()` to avoid the extra allocation",
88+
"consider using `.join()` to avoid the extra allocation",
89+
temp_string,
90+
Applicability::MaybeIncorrect,
7891
);
79-
// report your lint here
8092
}
8193
}
8294
}

tests/ui/path_from_format.stderr

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ error: `format!(..)` used to form `PathBuf`
22
--> $DIR/path_from_format.rs:9:5
33
|
44
LL | PathBuf::from(format!("{}/foo/bar", base_path1));
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `.join()` to avoid the extra allocation: `Path::new(base_path1).join("foo").join("bar")`
66
|
77
= note: `-D clippy::path-from-format` implied by `-D warnings`
8-
= help: consider using `.join()` or `.push()` to avoid the extra allocation
98

109
error: aborting due to previous error
1110

0 commit comments

Comments
 (0)