]> git.lizzy.rs Git - rust.git/blob - src/strings.rs
added helpful links to lints that have wiki entries
[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_help_and_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                 span_help_and_lint(cx, STRING_ADD, e.span,
49                     "you added something to a string. \
50                      Consider using `String::push_str()` instead",
51                     "for further information see https://github.com/\
52                      Manishearth/rust-clippy/wiki#string_add")
53             }
54         } else if let &ExprAssign(ref target, ref  src) = &e.node {
55             if is_string(cx, target) && is_add(cx, src, target) {
56                 span_help_and_lint(cx, STRING_ADD_ASSIGN, e.span,
57                     "you assigned the result of adding something to this string. \
58                      Consider using `String::push_str()` instead",
59                     "for further information see https://github.com/\
60                     Manishearth/rust-clippy/wiki#string_add_assign")
61             }
62         }
63     }
64 }
65
66 fn is_string(cx: &Context, e: &Expr) -> bool {
67     match_type(cx, walk_ptrs_ty(cx.tcx.expr_ty(e)), &STRING_PATH)
68 }
69
70 fn is_add(cx: &Context, src: &Expr, target: &Expr) -> bool {
71     match src.node {
72         ExprBinary(Spanned{ node: BiAdd, .. }, ref left, _) =>
73             is_exp_equal(cx, target, left),
74         ExprBlock(ref block) => block.stmts.is_empty() &&
75             block.expr.as_ref().map_or(false,
76                 |expr| is_add(cx, expr, target)),
77         ExprParen(ref expr) => is_add(cx, expr, target),
78         _ => false
79     }
80 }