]> git.lizzy.rs Git - rust.git/blob - src/strings.rs
d03f4d53c606b835a4e0a28f7997522b1ad2f479
[rust.git] / src / strings.rs
1 //! This LintPass catches both string addition and string addition + assignment
2 //!
3 //! Note that since we have two lints where one subsumes the other, we try to
4 //! disable the subsumed lint unless it has a higher level
5
6 use rustc::lint::*;
7 use syntax::ast::*;
8 use syntax::codemap::Spanned;
9
10 use eq_op::is_exp_equal;
11 use utils::{match_type, span_lint, walk_ptrs_ty, get_parent_expr};
12 use utils::STRING_PATH;
13
14 declare_lint! {
15     pub STRING_ADD_ASSIGN,
16     Allow,
17     "using `x = x + ..` where x is a `String`; suggests using `push_str()` instead"
18 }
19
20 declare_lint! {
21     pub STRING_ADD,
22     Allow,
23     "using `x + ..` where x is a `String`; suggests using `push_str()` instead"
24 }
25
26 #[derive(Copy, Clone)]
27 pub struct StringAdd;
28
29 impl LintPass for StringAdd {
30     fn get_lints(&self) -> LintArray {
31         lint_array!(STRING_ADD, STRING_ADD_ASSIGN)
32     }
33
34     fn check_expr(&mut self, cx: &Context, e: &Expr) {
35         if let &ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) = &e.node {
36             if is_string(cx, left) {
37                 if let Allow = cx.current_level(STRING_ADD_ASSIGN) {
38                     // the string_add_assign is allow, so no duplicates
39                 } else {
40                     let parent = get_parent_expr(cx, e);
41                     if let Some(ref p) = parent {
42                         if let &ExprAssign(ref target, _) = &p.node {
43                             // avoid duplicate matches
44                             if is_exp_equal(cx, target, left) { return; }
45                         }
46                     }
47                 }
48                 //TODO check for duplicates
49                  span_lint(cx, STRING_ADD, e.span,
50                         "you added something to a string. \
51                          Consider using `String::push_str()` instead")
52             }
53         } else if let &ExprAssign(ref target, ref  src) = &e.node {
54             if is_string(cx, target) && is_add(cx, src, target) {
55                 span_lint(cx, STRING_ADD_ASSIGN, e.span,
56                     "you assigned the result of adding something to this string. \
57                      Consider using `String::push_str()` instead")
58             }
59         }
60     }
61 }
62
63 fn is_string(cx: &Context, e: &Expr) -> bool {
64     match_type(cx, walk_ptrs_ty(cx.tcx.expr_ty(e)), &STRING_PATH)
65 }
66
67 fn is_add(cx: &Context, src: &Expr, target: &Expr) -> bool {
68     match src.node {
69         ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) =>
70             is_exp_equal(cx, target, left),
71         ExprBlock(ref block) => block.stmts.is_empty() &&
72             block.expr.as_ref().map_or(false,
73                 |expr| is_add(cx, expr, target)),
74         ExprParen(ref expr) => is_add(cx, expr, target),
75         _ => false
76     }
77 }