Add `for _ in vec![…]` to the `USELESS_VEC` lint

This commit is contained in:
mcarton 2016-03-28 23:32:55 +02:00
parent 11eae72e6f
commit 777e810a39
4 changed files with 67 additions and 50 deletions

View File

@ -14,7 +14,7 @@ use syntax::ast;
use utils::{snippet, span_lint, get_parent_expr, match_trait_method, match_type, in_external_macro, use utils::{snippet, span_lint, get_parent_expr, match_trait_method, match_type, in_external_macro,
span_help_and_lint, is_integer_literal, get_enclosing_block, span_lint_and_then, span_help_and_lint, is_integer_literal, get_enclosing_block, span_lint_and_then,
unsugar_range, walk_ptrs_ty}; unsugar_range, walk_ptrs_ty, recover_for_loop};
use utils::{BTREEMAP_PATH, HASHMAP_PATH, LL_PATH, OPTION_PATH, RESULT_PATH, VEC_PATH}; use utils::{BTREEMAP_PATH, HASHMAP_PATH, LL_PATH, OPTION_PATH, RESULT_PATH, VEC_PATH};
use utils::UnsugaredRange; use utils::UnsugaredRange;
@ -641,30 +641,6 @@ impl<'a> Visitor<'a> for UsedVisitor {
} }
} }
/// Recover the essential nodes of a desugared for loop:
/// `for pat in arg { body }` becomes `(pat, arg, body)`.
fn recover_for_loop(expr: &Expr) -> Option<(&Pat, &Expr, &Expr)> {
if_let_chain! {
[
let ExprMatch(ref iterexpr, ref arms, _) = expr.node,
let ExprCall(_, ref iterargs) = iterexpr.node,
iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none(),
let ExprLoop(ref block, _) = arms[0].body.node,
block.stmts.is_empty(),
let Some(ref loopexpr) = block.expr,
let ExprMatch(_, ref innerarms, MatchSource::ForLoopDesugar) = loopexpr.node,
innerarms.len() == 2 && innerarms[0].pats.len() == 1,
let PatKind::TupleStruct(_, Some(ref somepats)) = innerarms[0].pats[0].node,
somepats.len() == 1
], {
return Some((&somepats[0],
&iterargs[0],
&innerarms[0].body));
}
}
None
}
struct VarVisitor<'v, 't: 'v> { struct VarVisitor<'v, 't: 'v> {
cx: &'v LateContext<'v, 't>, // context reference cx: &'v LateContext<'v, 't>, // context reference
var: Name, // var name to look for as index var: Name, // var name to look for as index

View File

@ -800,3 +800,27 @@ pub fn same_tys<'a, 'tcx>(cx: &LateContext<'a, 'tcx>, a: ty::Ty<'tcx>, b: ty::Ty
let new_b = b.subst(infcx.tcx, &infcx.parameter_environment.free_substs); let new_b = b.subst(infcx.tcx, &infcx.parameter_environment.free_substs);
infcx.can_equate(&new_a, &new_b).is_ok() infcx.can_equate(&new_a, &new_b).is_ok()
} }
/// Recover the essential nodes of a desugared for loop:
/// `for pat in arg { body }` becomes `(pat, arg, body)`.
pub fn recover_for_loop(expr: &Expr) -> Option<(&Pat, &Expr, &Expr)> {
if_let_chain! {
[
let ExprMatch(ref iterexpr, ref arms, _) = expr.node,
let ExprCall(_, ref iterargs) = iterexpr.node,
iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none(),
let ExprLoop(ref block, _) = arms[0].body.node,
block.stmts.is_empty(),
let Some(ref loopexpr) = block.expr,
let ExprMatch(_, ref innerarms, MatchSource::ForLoopDesugar) = loopexpr.node,
innerarms.len() == 2 && innerarms[0].pats.len() == 1,
let PatKind::TupleStruct(_, Some(ref somepats)) = innerarms[0].pats[0].node,
somepats.len() == 1
], {
return Some((&somepats[0],
&iterargs[0],
&innerarms[0].body));
}
}
None
}

View File

@ -4,7 +4,7 @@ use rustc_front::hir::*;
use syntax::codemap::Span; use syntax::codemap::Span;
use syntax::ptr::P; use syntax::ptr::P;
use utils::VEC_FROM_ELEM_PATH; use utils::VEC_FROM_ELEM_PATH;
use utils::{is_expn_of, match_path, snippet, span_lint_and_then}; use utils::{is_expn_of, match_path, recover_for_loop, snippet, span_lint_and_then};
/// **What it does:** This lint warns about using `&vec![..]` when using `&[..]` would be possible. /// **What it does:** This lint warns about using `&vec![..]` when using `&[..]` would be possible.
/// ///
@ -38,32 +38,42 @@ impl LateLintPass for UselessVec {
let TypeVariants::TyRef(_, ref ty) = cx.tcx.expr_ty_adjusted(expr).sty, let TypeVariants::TyRef(_, ref ty) = cx.tcx.expr_ty_adjusted(expr).sty,
let TypeVariants::TySlice(..) = ty.ty.sty, let TypeVariants::TySlice(..) = ty.ty.sty,
let ExprAddrOf(_, ref addressee) = expr.node, let ExprAddrOf(_, ref addressee) = expr.node,
let Some(vec_args) = unexpand_vec(cx, addressee)
], { ], {
let snippet = match vec_args { check_vec_macro(cx, expr, addressee);
VecArgs::Repeat(elem, len) => {
format!("&[{}; {}]", snippet(cx, elem.span, "elem"), snippet(cx, len.span, "len")).into()
}
VecArgs::Vec(args) => {
if let Some(last) = args.iter().last() {
let span = Span {
lo: args[0].span.lo,
hi: last.span.hi,
expn_id: args[0].span.expn_id,
};
format!("&[{}]", snippet(cx, span, "..")).into()
}
else {
"&[]".into()
}
}
};
span_lint_and_then(cx, USELESS_VEC, expr.span, "useless use of `vec!`", |db| {
db.span_suggestion(expr.span, "you can use a slice directly", snippet);
});
}} }}
// search for `for _ in vec![…]`
if let Some((_, arg, _)) = recover_for_loop(expr) {
check_vec_macro(cx, arg, arg);
}
}
}
fn check_vec_macro(cx: &LateContext, expr: &Expr, vec: &Expr) {
if let Some(vec_args) = unexpand_vec(cx, vec) {
let snippet = match vec_args {
VecArgs::Repeat(elem, len) => {
format!("&[{}; {}]", snippet(cx, elem.span, "elem"), snippet(cx, len.span, "len")).into()
}
VecArgs::Vec(args) => {
if let Some(last) = args.iter().last() {
let span = Span {
lo: args[0].span.lo,
hi: last.span.hi,
expn_id: args[0].span.expn_id,
};
format!("&[{}]", snippet(cx, span, "..")).into()
}
else {
"&[]".into()
}
}
};
span_lint_and_then(cx, USELESS_VEC, expr.span, "useless use of `vec!`", |db| {
db.span_suggestion(expr.span, "you can use a slice directly", snippet);
});
} }
} }

View File

@ -41,4 +41,11 @@ fn main() {
on_vec(&vec![]); on_vec(&vec![]);
on_vec(&vec![1, 2]); on_vec(&vec![1, 2]);
on_vec(&vec![1; 2]); on_vec(&vec![1; 2]);
for a in vec![1, 2, 3] {
//~^ ERROR useless use of `vec!`
//~| HELP you can use
//~| SUGGESTION for a in &[1, 2, 3] {
println!("{}", a);
}
} }