start: Some(start),
end: Some(end),
limits,
- }) = higher::range(cx, arg)
+ }) = higher::range(arg)
{
// the var must be a single name
if let PatKind::Binding(_, canonical_id, _, _) = pat.kind {
body: &'tcx Expr<'_>,
_: &'tcx Expr<'_>,
) {
+ fn emit_lint(cx: &LateContext<'_>, vec: &Expr<'_>, pushed_item: &Expr<'_>) {
+ let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
+ let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
+
+ span_lint_and_help(
+ cx,
+ SAME_ITEM_PUSH,
+ vec.span,
+ "it looks like the same item is being pushed into this Vec",
+ None,
+ &format!(
+ "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
+ item_str, vec_str, item_str
+ ),
+ )
+ }
+
+ if !matches!(pat.kind, PatKind::Wild) {
+ return;
+ }
+
// Determine whether it is safe to lint the body
let mut same_item_push_visitor = SameItemPushVisitor {
should_lint: true,
walk_expr(&mut same_item_push_visitor, body);
if same_item_push_visitor.should_lint {
if let Some((vec, pushed_item)) = same_item_push_visitor.vec_push {
- // Make sure that the push does not involve possibly mutating values
- if mutated_variables(pushed_item, cx).map_or(false, |mutvars| mutvars.is_empty()) {
- if let PatKind::Wild = pat.kind {
- let vec_str = snippet_with_macro_callsite(cx, vec.span, "");
- let item_str = snippet_with_macro_callsite(cx, pushed_item.span, "");
-
- span_lint_and_help(
- cx,
- SAME_ITEM_PUSH,
- vec.span,
- "it looks like the same item is being pushed into this Vec",
- None,
- &format!(
- "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})",
- item_str, vec_str, item_str
- ),
- )
+ let vec_ty = cx.typeck_results().expr_ty(vec);
+ let ty = vec_ty.walk().nth(1).unwrap().expect_ty();
+ if cx
+ .tcx
+ .lang_items()
+ .clone_trait()
+ .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+ {
+ // Make sure that the push does not involve possibly mutating values
+ match pushed_item.kind {
+ ExprKind::Path(ref qpath) => {
+ match qpath_res(cx, qpath, pushed_item.hir_id) {
+ // immutable bindings that are initialized with literal or constant
+ Res::Local(hir_id) => {
+ if_chain! {
+ let node = cx.tcx.hir().get(hir_id);
+ if let Node::Binding(pat) = node;
+ if let PatKind::Binding(bind_ann, ..) = pat.kind;
+ if !matches!(bind_ann, BindingAnnotation::RefMut | BindingAnnotation::Mutable);
+ let parent_node = cx.tcx.hir().get_parent_node(hir_id);
+ if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node);
+ if let Some(init) = parent_let_expr.init;
+ then {
+ match init.kind {
+ // immutable bindings that are initialized with literal
+ ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
+ // immutable bindings that are initialized with constant
+ ExprKind::Path(ref path) => {
+ if let Res::Def(DefKind::Const, ..) = qpath_res(cx, path, init.hir_id) {
+ emit_lint(cx, vec, pushed_item);
+ }
+ }
+ _ => {},
+ }
+ }
+ }
+ },
+ // constant
+ Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item),
+ _ => {},
+ }
+ },
+ ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item),
+ _ => {},
}
}
}
start: Some(start),
ref end,
limits,
- }) = higher::range(cx, arg)
+ }) = higher::range(arg)
{
// the var must be a single name
if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind {
start: Some(start),
end: Some(end),
..
- }) = higher::range(cx, arg)
+ }) = higher::range(arg)
{
let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)];
if mut_ids[0].is_some() || mut_ids[1].is_some() {