use syntax::ast::*;
use rustc::lint::*;
use rustc::middle::ty;
+use std::iter;
+use std::borrow::Cow;
-use utils::{span_lint, match_type, walk_ptrs_ty};
+use utils::{snippet, span_lint, match_type, walk_ptrs_ty_depth};
use utils::{OPTION_PATH, RESULT_PATH, STRING_PATH};
#[derive(Copy,Clone)]
fn check_expr(&mut self, cx: &Context, expr: &Expr) {
if let ExprMethodCall(ref ident, _, ref args) = expr.node {
- let obj_ty = walk_ptrs_ty(cx.tcx.expr_ty(&args[0]));
+ let (obj_ty, ptr_depth) = walk_ptrs_ty_depth(cx.tcx.expr_ty(&args[0]));
if ident.node.name == "unwrap" {
if match_type(cx, obj_ty, &OPTION_PATH) {
span_lint(cx, OPTION_UNWRAP_USED, expr.span,
}
else if ident.node.name == "to_string" {
if obj_ty.sty == ty::TyStr {
- span_lint(cx, STR_TO_STRING, expr.span, "`str.to_owned()` is faster");
+ let mut arg_str = snippet(cx, args[0].span, "_");
+ if ptr_depth > 1 {
+ arg_str = Cow::Owned(format!(
+ "({}{})",
+ iter::repeat('*').take(ptr_depth - 1).collect::<String>(),
+ arg_str));
+ }
+ span_lint(cx, STR_TO_STRING, expr.span, &format!(
+ "`{}.to_owned()` is faster", arg_str));
} else if match_type(cx, obj_ty, &STRING_PATH) {
span_lint(cx, STRING_TO_STRING, expr.span, "`String.to_string()` is a no-op; use \
`clone()` to make a copy");
}
}
+/// return the base type for references and raw pointers, and count reference depth
+pub fn walk_ptrs_ty_depth(ty: ty::Ty) -> (ty::Ty, usize) {
+ fn inner(ty: ty::Ty, depth: usize) -> (ty::Ty, usize) {
+ match ty.sty {
+ ty::TyRef(_, ref tm) | ty::TyRawPtr(ref tm) => inner(tm.ty, depth + 1),
+ _ => (ty, depth)
+ }
+ }
+ inner(ty, 0)
+}
+
/// Produce a nested chain of if-lets and ifs from the patterns:
///
/// if_let_chain! {
let res: Result<i32, ()> = Ok(0);
let _ = res.unwrap(); //~ERROR used unwrap() on a Result
- let string = "str".to_string(); //~ERROR `str.to_owned()` is faster
+ let _ = "str".to_string(); //~ERROR `"str".to_owned()` is faster
+
+ let v = &"str";
+ let string = v.to_string(); //~ERROR `(*v).to_owned()` is faster
let _again = string.to_string(); //~ERROR `String.to_string()` is a no-op
}
#[deny(string_add)]
#[allow(string_add_assign)]
fn add_only() { // ignores assignment distinction
- let mut x = "".to_owned();
+ let mut x = "".to_owned();
for _ in (1..3) {
x = x + "."; //~ERROR you added something to a string.
}
-
+
let y = "".to_owned();
let z = y + "..."; //~ERROR you added something to a string.
-
+
assert_eq!(&x, &z);
}
#[deny(string_add_assign)]
fn add_assign_only() {
- let mut x = "".to_owned();
+ let mut x = "".to_owned();
for _ in (1..3) {
x = x + "."; //~ERROR you assigned the result of adding something to this string.
}
-
+
let y = "".to_owned();
let z = y + "...";
-
+
assert_eq!(&x, &z);
}
for _ in (1..3) {
x = x + "."; //~ERROR you assigned the result of adding something to this string.
}
-
+
let y = "".to_owned();
let z = y + "..."; //~ERROR you added something to a string.
-
+
assert_eq!(&x, &z);
}
fn main() {
- add_only();
- add_assign_only();
- both();
-
- // the add is only caught for String
- let mut x = 1;
- x = x + 1;
- assert_eq!(2, x);
+ add_only();
+ add_assign_only();
+ both();
+
+ // the add is only caught for String
+ let mut x = 1;
+ x = x + 1;
+ assert_eq!(2, x);
}