@@ -1131,6 +1131,27 @@ fn detect_same_item_push<'tcx>(
11311131 body : & ' tcx Expr < ' _ > ,
11321132 _: & ' tcx Expr < ' _ > ,
11331133) {
1134+ fn emit_lint ( cx : & LateContext < ' _ > , vec : & Expr < ' _ > , pushed_item : & Expr < ' _ > ) {
1135+ let vec_str = snippet_with_macro_callsite ( cx, vec. span , "" ) ;
1136+ let item_str = snippet_with_macro_callsite ( cx, pushed_item. span , "" ) ;
1137+
1138+ span_lint_and_help (
1139+ cx,
1140+ SAME_ITEM_PUSH ,
1141+ vec. span ,
1142+ "it looks like the same item is being pushed into this Vec" ,
1143+ None ,
1144+ & format ! (
1145+ "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})" ,
1146+ item_str, vec_str, item_str
1147+ ) ,
1148+ )
1149+ }
1150+
1151+ if !matches ! ( pat. kind, PatKind :: Wild ) {
1152+ return ;
1153+ }
1154+
11341155 // Determine whether it is safe to lint the body
11351156 let mut same_item_push_visitor = SameItemPushVisitor {
11361157 should_lint : true ,
@@ -1140,23 +1161,50 @@ fn detect_same_item_push<'tcx>(
11401161 walk_expr ( & mut same_item_push_visitor, body) ;
11411162 if same_item_push_visitor. should_lint {
11421163 if let Some ( ( vec, pushed_item) ) = same_item_push_visitor. vec_push {
1143- // Make sure that the push does not involve possibly mutating values
1144- if mutated_variables ( pushed_item, cx) . map_or ( false , |mutvars| mutvars. is_empty ( ) ) {
1145- if let PatKind :: Wild = pat. kind {
1146- let vec_str = snippet_with_macro_callsite ( cx, vec. span , "" ) ;
1147- let item_str = snippet_with_macro_callsite ( cx, pushed_item. span , "" ) ;
1148-
1149- span_lint_and_help (
1150- cx,
1151- SAME_ITEM_PUSH ,
1152- vec. span ,
1153- "it looks like the same item is being pushed into this Vec" ,
1154- None ,
1155- & format ! (
1156- "try using vec![{};SIZE] or {}.resize(NEW_SIZE, {})" ,
1157- item_str, vec_str, item_str
1158- ) ,
1159- )
1164+ let vec_ty = cx. typeck_results ( ) . expr_ty ( vec) ;
1165+ let ty = vec_ty. walk ( ) . nth ( 1 ) . unwrap ( ) . expect_ty ( ) ;
1166+ if cx
1167+ . tcx
1168+ . lang_items ( )
1169+ . clone_trait ( )
1170+ . map_or ( false , |id| implements_trait ( cx, ty, id, & [ ] ) )
1171+ {
1172+ // Make sure that the push does not involve possibly mutating values
1173+ match pushed_item. kind {
1174+ ExprKind :: Path ( ref qpath) => {
1175+ match qpath_res ( cx, qpath, pushed_item. hir_id ) {
1176+ // immutable bindings that are initialized with literal or constant
1177+ Res :: Local ( hir_id) => {
1178+ if_chain ! {
1179+ let node = cx. tcx. hir( ) . get( hir_id) ;
1180+ if let Node :: Binding ( pat) = node;
1181+ if let PatKind :: Binding ( bind_ann, ..) = pat. kind;
1182+ if !matches!( bind_ann, BindingAnnotation :: RefMut | BindingAnnotation :: Mutable ) ;
1183+ let parent_node = cx. tcx. hir( ) . get_parent_node( hir_id) ;
1184+ if let Some ( Node :: Local ( parent_let_expr) ) = cx. tcx. hir( ) . find( parent_node) ;
1185+ if let Some ( init) = parent_let_expr. init;
1186+ then {
1187+ match init. kind {
1188+ // immutable bindings that are initialized with literal
1189+ ExprKind :: Lit ( ..) => emit_lint( cx, vec, pushed_item) ,
1190+ // immutable bindings that are initialized with constant
1191+ ExprKind :: Path ( ref path) => {
1192+ if let Res :: Def ( DefKind :: Const , ..) = qpath_res( cx, path, init. hir_id) {
1193+ emit_lint( cx, vec, pushed_item) ;
1194+ }
1195+ }
1196+ _ => { } ,
1197+ }
1198+ }
1199+ }
1200+ } ,
1201+ // constant
1202+ Res :: Def ( DefKind :: Const , ..) => emit_lint ( cx, vec, pushed_item) ,
1203+ _ => { } ,
1204+ }
1205+ } ,
1206+ ExprKind :: Lit ( ..) => emit_lint ( cx, vec, pushed_item) ,
1207+ _ => { } ,
11601208 }
11611209 }
11621210 }
0 commit comments