1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use rustc::hir::*;
use rustc::hir::map::Node::NodeItem;
use rustc::lint::*;
use rustc::ty::TypeVariants;
use syntax::ast::LitKind;
use syntax::symbol::InternedString;
use utils::paths;
use utils::{is_expn_of, match_def_path, match_type, resolve_node, span_lint, walk_ptrs_ty};
declare_lint! {
pub USELESS_FORMAT,
Warn,
"useless use of `format!`"
}
#[derive(Copy, Clone, Debug)]
pub struct Pass;
impl LintPass for Pass {
fn get_lints(&self) -> LintArray {
lint_array![USELESS_FORMAT]
}
}
impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
fn check_expr(&mut self, cx: &LateContext<'a, 'tcx>, expr: &'tcx Expr) {
if let Some(span) = is_expn_of(cx, expr.span, "format") {
match expr.node {
ExprCall(ref fun, ref args) => {
if_let_chain!{[
let ExprPath(ref qpath) = fun.node,
args.len() == 2,
match_def_path(cx, resolve_node(cx, qpath, fun.id).def_id(), &paths::FMT_ARGUMENTS_NEWV1),
check_static_str(cx, &args[0]),
check_arg_is_display(cx, &args[1])
], {
span_lint(cx, USELESS_FORMAT, span, "useless use of `format!`");
}}
},
ExprMatch(ref matchee, _, _) => {
if let ExprTup(ref tup) = matchee.node {
if tup.is_empty() {
span_lint(cx, USELESS_FORMAT, span, "useless use of `format!`");
}
}
},
_ => (),
}
}
}
}
pub fn get_argument_fmtstr_parts<'a, 'b>(cx: &LateContext<'a, 'b>, expr: &'a Expr) -> Option<Vec<InternedString>> {
if_let_chain! {[
let ExprBlock(ref block) = expr.node,
block.stmts.len() == 1,
let StmtDecl(ref decl, _) = block.stmts[0].node,
let DeclItem(ref decl) = decl.node,
let Some(NodeItem(decl)) = cx.tcx.map.find(decl.id),
&*decl.name.as_str() == "__STATIC_FMTSTR",
let ItemStatic(_, _, ref expr) = decl.node,
let ExprAddrOf(_, ref expr) = cx.tcx.map.body(*expr).value.node,
let ExprArray(ref exprs) = expr.node,
], {
let mut result = Vec::new();
for expr in exprs {
if let ExprLit(ref lit) = expr.node {
if let LitKind::Str(ref lit, _) = lit.node {
result.push(lit.as_str());
}
}
}
return Some(result);
}}
None
}
fn check_static_str(cx: &LateContext, expr: &Expr) -> bool {
if let Some(expr) = get_argument_fmtstr_parts(cx, expr) {
expr.len() == 1 && expr[0].is_empty()
} else {
false
}
}
fn check_arg_is_display(cx: &LateContext, expr: &Expr) -> bool {
if_let_chain! {[
let ExprAddrOf(_, ref expr) = expr.node,
let ExprMatch(_, ref arms, _) = expr.node,
arms.len() == 1,
arms[0].pats.len() == 1,
let PatKind::Tuple(ref pat, None) = arms[0].pats[0].node,
pat.len() == 1,
let ExprArray(ref exprs) = arms[0].body.node,
exprs.len() == 1,
let ExprCall(_, ref args) = exprs[0].node,
args.len() == 2,
let ExprPath(ref qpath) = args[1].node,
match_def_path(cx, resolve_node(cx, qpath, args[1].id).def_id(), &paths::DISPLAY_FMT_METHOD),
], {
let ty = walk_ptrs_ty(cx.tcx.tables().pat_ty(&pat[0]));
return ty.sty == TypeVariants::TyStr || match_type(cx, ty, &paths::STRING);
}}
false
}