]> git.lizzy.rs Git - rust.git/blob - src/librustc_lint/unused.rs
Rollup merge of #68050 - Centril:canon-error, r=Mark-Simulacrum
[rust.git] / src / librustc_lint / unused.rs
1 use lint::{EarlyContext, LateContext, LintArray, LintContext};
2 use lint::{EarlyLintPass, LateLintPass, LintPass};
3 use rustc::lint;
4 use rustc::lint::builtin::UNUSED_ATTRIBUTES;
5 use rustc::ty::adjustment;
6 use rustc::ty::{self, Ty};
7 use rustc_data_structures::fx::FxHashMap;
8 use rustc_errors::{pluralize, Applicability};
9 use rustc_feature::{AttributeType, BuiltinAttribute, BUILTIN_ATTRIBUTE_MAP};
10 use rustc_hir as hir;
11 use rustc_hir::def::{DefKind, Res};
12 use rustc_hir::def_id::DefId;
13 use rustc_span::symbol::Symbol;
14 use rustc_span::symbol::{kw, sym};
15 use rustc_span::{BytePos, Span};
16 use syntax::ast;
17 use syntax::attr;
18 use syntax::print::pprust;
19 use syntax::util::parser;
20
21 use log::debug;
22
23 declare_lint! {
24     pub UNUSED_MUST_USE,
25     Warn,
26     "unused result of a type flagged as `#[must_use]`",
27     report_in_external_macro
28 }
29
30 declare_lint! {
31     pub UNUSED_RESULTS,
32     Allow,
33     "unused result of an expression in a statement"
34 }
35
36 declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]);
37
38 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults {
39     fn check_stmt(&mut self, cx: &LateContext<'_, '_>, s: &hir::Stmt<'_>) {
40         let expr = match s.kind {
41             hir::StmtKind::Semi(ref expr) => &**expr,
42             _ => return,
43         };
44
45         if let hir::ExprKind::Ret(..) = expr.kind {
46             return;
47         }
48
49         let ty = cx.tables.expr_ty(&expr);
50         let type_permits_lack_of_use = check_must_use_ty(cx, ty, &expr, s.span, "", "", 1);
51
52         let mut fn_warned = false;
53         let mut op_warned = false;
54         let maybe_def_id = match expr.kind {
55             hir::ExprKind::Call(ref callee, _) => {
56                 match callee.kind {
57                     hir::ExprKind::Path(ref qpath) => {
58                         match cx.tables.qpath_res(qpath, callee.hir_id) {
59                             Res::Def(DefKind::Fn, def_id) | Res::Def(DefKind::Method, def_id) => {
60                                 Some(def_id)
61                             }
62                             // `Res::Local` if it was a closure, for which we
63                             // do not currently support must-use linting
64                             _ => None,
65                         }
66                     }
67                     _ => None,
68                 }
69             }
70             hir::ExprKind::MethodCall(..) => cx.tables.type_dependent_def_id(expr.hir_id),
71             _ => None,
72         };
73         if let Some(def_id) = maybe_def_id {
74             fn_warned = check_must_use_def(cx, def_id, s.span, "return value of ", "");
75         } else if type_permits_lack_of_use {
76             // We don't warn about unused unit or uninhabited types.
77             // (See https://github.com/rust-lang/rust/issues/43806 for details.)
78             return;
79         }
80
81         let must_use_op = match expr.kind {
82             // Hardcoding operators here seemed more expedient than the
83             // refactoring that would be needed to look up the `#[must_use]`
84             // attribute which does exist on the comparison trait methods
85             hir::ExprKind::Binary(bin_op, ..) => match bin_op.node {
86                 hir::BinOpKind::Eq
87                 | hir::BinOpKind::Lt
88                 | hir::BinOpKind::Le
89                 | hir::BinOpKind::Ne
90                 | hir::BinOpKind::Ge
91                 | hir::BinOpKind::Gt => Some("comparison"),
92                 hir::BinOpKind::Add
93                 | hir::BinOpKind::Sub
94                 | hir::BinOpKind::Div
95                 | hir::BinOpKind::Mul
96                 | hir::BinOpKind::Rem => Some("arithmetic operation"),
97                 hir::BinOpKind::And | hir::BinOpKind::Or => Some("logical operation"),
98                 hir::BinOpKind::BitXor
99                 | hir::BinOpKind::BitAnd
100                 | hir::BinOpKind::BitOr
101                 | hir::BinOpKind::Shl
102                 | hir::BinOpKind::Shr => Some("bitwise operation"),
103             },
104             hir::ExprKind::Unary(..) => Some("unary operation"),
105             _ => None,
106         };
107
108         if let Some(must_use_op) = must_use_op {
109             cx.span_lint(
110                 UNUSED_MUST_USE,
111                 expr.span,
112                 &format!("unused {} that must be used", must_use_op),
113             );
114             op_warned = true;
115         }
116
117         if !(type_permits_lack_of_use || fn_warned || op_warned) {
118             cx.span_lint(UNUSED_RESULTS, s.span, "unused result");
119         }
120
121         // Returns whether an error has been emitted (and thus another does not need to be later).
122         fn check_must_use_ty<'tcx>(
123             cx: &LateContext<'_, 'tcx>,
124             ty: Ty<'tcx>,
125             expr: &hir::Expr<'_>,
126             span: Span,
127             descr_pre: &str,
128             descr_post: &str,
129             plural_len: usize,
130         ) -> bool {
131             if ty.is_unit()
132                 || cx.tcx.is_ty_uninhabited_from(cx.tcx.hir().get_module_parent(expr.hir_id), ty)
133             {
134                 return true;
135             }
136
137             let plural_suffix = pluralize!(plural_len);
138
139             match ty.kind {
140                 ty::Adt(..) if ty.is_box() => {
141                     let boxed_ty = ty.boxed_ty();
142                     let descr_pre = &format!("{}boxed ", descr_pre);
143                     check_must_use_ty(cx, boxed_ty, expr, span, descr_pre, descr_post, plural_len)
144                 }
145                 ty::Adt(def, _) => check_must_use_def(cx, def.did, span, descr_pre, descr_post),
146                 ty::Opaque(def, _) => {
147                     let mut has_emitted = false;
148                     for (predicate, _) in cx.tcx.predicates_of(def).predicates {
149                         if let ty::Predicate::Trait(ref poly_trait_predicate) = predicate {
150                             let trait_ref = poly_trait_predicate.skip_binder().trait_ref;
151                             let def_id = trait_ref.def_id;
152                             let descr_pre =
153                                 &format!("{}implementer{} of ", descr_pre, plural_suffix,);
154                             if check_must_use_def(cx, def_id, span, descr_pre, descr_post) {
155                                 has_emitted = true;
156                                 break;
157                             }
158                         }
159                     }
160                     has_emitted
161                 }
162                 ty::Dynamic(binder, _) => {
163                     let mut has_emitted = false;
164                     for predicate in binder.skip_binder().iter() {
165                         if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate {
166                             let def_id = trait_ref.def_id;
167                             let descr_post =
168                                 &format!(" trait object{}{}", plural_suffix, descr_post,);
169                             if check_must_use_def(cx, def_id, span, descr_pre, descr_post) {
170                                 has_emitted = true;
171                                 break;
172                             }
173                         }
174                     }
175                     has_emitted
176                 }
177                 ty::Tuple(ref tys) => {
178                     let mut has_emitted = false;
179                     let spans = if let hir::ExprKind::Tup(comps) = &expr.kind {
180                         debug_assert_eq!(comps.len(), tys.len());
181                         comps.iter().map(|e| e.span).collect()
182                     } else {
183                         vec![]
184                     };
185                     for (i, ty) in tys.iter().map(|k| k.expect_ty()).enumerate() {
186                         let descr_post = &format!(" in tuple element {}", i);
187                         let span = *spans.get(i).unwrap_or(&span);
188                         if check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, plural_len)
189                         {
190                             has_emitted = true;
191                         }
192                     }
193                     has_emitted
194                 }
195                 ty::Array(ty, len) => match len.try_eval_usize(cx.tcx, cx.param_env) {
196                     // If the array is definitely non-empty, we can do `#[must_use]` checking.
197                     Some(n) if n != 0 => {
198                         let descr_pre = &format!("{}array{} of ", descr_pre, plural_suffix,);
199                         check_must_use_ty(cx, ty, expr, span, descr_pre, descr_post, n as usize + 1)
200                     }
201                     // Otherwise, we don't lint, to avoid false positives.
202                     _ => false,
203                 },
204                 _ => false,
205             }
206         }
207
208         // Returns whether an error has been emitted (and thus another does not need to be later).
209         fn check_must_use_def(
210             cx: &LateContext<'_, '_>,
211             def_id: DefId,
212             span: Span,
213             descr_pre_path: &str,
214             descr_post_path: &str,
215         ) -> bool {
216             for attr in cx.tcx.get_attrs(def_id).iter() {
217                 if attr.check_name(sym::must_use) {
218                     let msg = format!(
219                         "unused {}`{}`{} that must be used",
220                         descr_pre_path,
221                         cx.tcx.def_path_str(def_id),
222                         descr_post_path
223                     );
224                     let mut err = cx.struct_span_lint(UNUSED_MUST_USE, span, &msg);
225                     // check for #[must_use = "..."]
226                     if let Some(note) = attr.value_str() {
227                         err.note(&note.as_str());
228                     }
229                     err.emit();
230                     return true;
231                 }
232             }
233             false
234         }
235     }
236 }
237
238 declare_lint! {
239     pub PATH_STATEMENTS,
240     Warn,
241     "path statements with no effect"
242 }
243
244 declare_lint_pass!(PathStatements => [PATH_STATEMENTS]);
245
246 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for PathStatements {
247     fn check_stmt(&mut self, cx: &LateContext<'_, '_>, s: &hir::Stmt<'_>) {
248         if let hir::StmtKind::Semi(ref expr) = s.kind {
249             if let hir::ExprKind::Path(_) = expr.kind {
250                 cx.span_lint(PATH_STATEMENTS, s.span, "path statement with no effect");
251             }
252         }
253     }
254 }
255
256 #[derive(Copy, Clone)]
257 pub struct UnusedAttributes {
258     builtin_attributes: &'static FxHashMap<Symbol, &'static BuiltinAttribute>,
259 }
260
261 impl UnusedAttributes {
262     pub fn new() -> Self {
263         UnusedAttributes { builtin_attributes: &*BUILTIN_ATTRIBUTE_MAP }
264     }
265 }
266
267 impl_lint_pass!(UnusedAttributes => [UNUSED_ATTRIBUTES]);
268
269 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAttributes {
270     fn check_attribute(&mut self, cx: &LateContext<'_, '_>, attr: &ast::Attribute) {
271         debug!("checking attribute: {:?}", attr);
272
273         if attr.is_doc_comment() {
274             return;
275         }
276
277         let attr_info = attr.ident().and_then(|ident| self.builtin_attributes.get(&ident.name));
278
279         if let Some(&&(name, ty, ..)) = attr_info {
280             match ty {
281                 AttributeType::Whitelisted => {
282                     debug!("{:?} is Whitelisted", name);
283                     return;
284                 }
285                 _ => (),
286             }
287         }
288
289         if !attr::is_used(attr) {
290             debug!("emitting warning for: {:?}", attr);
291             cx.span_lint(UNUSED_ATTRIBUTES, attr.span, "unused attribute");
292             // Is it a builtin attribute that must be used at the crate level?
293             if attr_info.map_or(false, |(_, ty, ..)| ty == &AttributeType::CrateLevel) {
294                 let msg = match attr.style {
295                     ast::AttrStyle::Outer => {
296                         "crate-level attribute should be an inner attribute: add an exclamation \
297                          mark: `#![foo]`"
298                     }
299                     ast::AttrStyle::Inner => "crate-level attribute should be in the root module",
300                 };
301                 cx.span_lint(UNUSED_ATTRIBUTES, attr.span, msg);
302             }
303         } else {
304             debug!("Attr was used: {:?}", attr);
305         }
306     }
307 }
308
309 declare_lint! {
310     pub(super) UNUSED_PARENS,
311     Warn,
312     "`if`, `match`, `while` and `return` do not need parentheses"
313 }
314
315 declare_lint_pass!(UnusedParens => [UNUSED_PARENS]);
316
317 impl UnusedParens {
318     fn is_expr_parens_necessary(inner: &ast::Expr, followed_by_block: bool) -> bool {
319         followed_by_block
320             && match inner.kind {
321                 ast::ExprKind::Ret(_) | ast::ExprKind::Break(..) => true,
322                 _ => parser::contains_exterior_struct_lit(&inner),
323             }
324     }
325
326     fn check_unused_parens_expr(
327         &self,
328         cx: &EarlyContext<'_>,
329         value: &ast::Expr,
330         msg: &str,
331         followed_by_block: bool,
332         left_pos: Option<BytePos>,
333         right_pos: Option<BytePos>,
334     ) {
335         match value.kind {
336             ast::ExprKind::Paren(ref inner) => {
337                 if !Self::is_expr_parens_necessary(inner, followed_by_block)
338                     && value.attrs.is_empty()
339                     && !value.span.from_expansion()
340                 {
341                     let expr_text =
342                         if let Ok(snippet) = cx.sess().source_map().span_to_snippet(value.span) {
343                             snippet
344                         } else {
345                             pprust::expr_to_string(value)
346                         };
347                     let keep_space = (
348                         left_pos.map(|s| s >= value.span.lo()).unwrap_or(false),
349                         right_pos.map(|s| s <= value.span.hi()).unwrap_or(false),
350                     );
351                     Self::remove_outer_parens(cx, value.span, &expr_text, msg, keep_space);
352                 }
353             }
354             ast::ExprKind::Let(_, ref expr) => {
355                 // FIXME(#60336): Properly handle `let true = (false && true)`
356                 // actually needing the parenthesis.
357                 self.check_unused_parens_expr(
358                     cx,
359                     expr,
360                     "`let` head expression",
361                     followed_by_block,
362                     None,
363                     None,
364                 );
365             }
366             _ => {}
367         }
368     }
369
370     fn check_unused_parens_pat(
371         &self,
372         cx: &EarlyContext<'_>,
373         value: &ast::Pat,
374         avoid_or: bool,
375         avoid_mut: bool,
376     ) {
377         use ast::{BindingMode, Mutability, PatKind};
378
379         if let PatKind::Paren(inner) = &value.kind {
380             match inner.kind {
381                 // The lint visitor will visit each subpattern of `p`. We do not want to lint
382                 // any range pattern no matter where it occurs in the pattern. For something like
383                 // `&(a..=b)`, there is a recursive `check_pat` on `a` and `b`, but we will assume
384                 // that if there are unnecessary parens they serve a purpose of readability.
385                 PatKind::Range(..) => return,
386                 // Avoid `p0 | .. | pn` if we should.
387                 PatKind::Or(..) if avoid_or => return,
388                 // Avoid `mut x` and `mut x @ p` if we should:
389                 PatKind::Ident(BindingMode::ByValue(Mutability::Mut), ..) if avoid_mut => return,
390                 // Otherwise proceed with linting.
391                 _ => {}
392             }
393
394             let pattern_text =
395                 if let Ok(snippet) = cx.sess().source_map().span_to_snippet(value.span) {
396                     snippet
397                 } else {
398                     pprust::pat_to_string(value)
399                 };
400             Self::remove_outer_parens(cx, value.span, &pattern_text, "pattern", (false, false));
401         }
402     }
403
404     fn remove_outer_parens(
405         cx: &EarlyContext<'_>,
406         span: Span,
407         pattern: &str,
408         msg: &str,
409         keep_space: (bool, bool),
410     ) {
411         let span_msg = format!("unnecessary parentheses around {}", msg);
412         let mut err = cx.struct_span_lint(UNUSED_PARENS, span, &span_msg);
413         let mut ate_left_paren = false;
414         let mut ate_right_paren = false;
415         let parens_removed = pattern.trim_matches(|c| match c {
416             '(' => {
417                 if ate_left_paren {
418                     false
419                 } else {
420                     ate_left_paren = true;
421                     true
422                 }
423             }
424             ')' => {
425                 if ate_right_paren {
426                     false
427                 } else {
428                     ate_right_paren = true;
429                     true
430                 }
431             }
432             _ => false,
433         });
434
435         let replace = {
436             let mut replace = if keep_space.0 {
437                 let mut s = String::from(" ");
438                 s.push_str(parens_removed);
439                 s
440             } else {
441                 String::from(parens_removed)
442             };
443
444             if keep_space.1 {
445                 replace.push(' ');
446             }
447             replace
448         };
449
450         err.span_suggestion_short(
451             span,
452             "remove these parentheses",
453             replace,
454             Applicability::MachineApplicable,
455         );
456         err.emit();
457     }
458 }
459
460 impl EarlyLintPass for UnusedParens {
461     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
462         use syntax::ast::ExprKind::*;
463         let (value, msg, followed_by_block, left_pos, right_pos) = match e.kind {
464             Let(ref pat, ..) => {
465                 self.check_unused_parens_pat(cx, pat, false, false);
466                 return;
467             }
468
469             If(ref cond, ref block, ..) => {
470                 let left = e.span.lo() + rustc_span::BytePos(2);
471                 let right = block.span.lo();
472                 (cond, "`if` condition", true, Some(left), Some(right))
473             }
474
475             While(ref cond, ref block, ..) => {
476                 let left = e.span.lo() + rustc_span::BytePos(5);
477                 let right = block.span.lo();
478                 (cond, "`while` condition", true, Some(left), Some(right))
479             }
480
481             ForLoop(ref pat, ref cond, ref block, ..) => {
482                 self.check_unused_parens_pat(cx, pat, false, false);
483                 (cond, "`for` head expression", true, None, Some(block.span.lo()))
484             }
485
486             Match(ref head, _) => {
487                 let left = e.span.lo() + rustc_span::BytePos(5);
488                 (head, "`match` head expression", true, Some(left), None)
489             }
490
491             Ret(Some(ref value)) => {
492                 let left = e.span.lo() + rustc_span::BytePos(3);
493                 (value, "`return` value", false, Some(left), None)
494             }
495
496             Assign(_, ref value, _) => (value, "assigned value", false, None, None),
497             AssignOp(.., ref value) => (value, "assigned value", false, None, None),
498             // either function/method call, or something this lint doesn't care about
499             ref call_or_other => {
500                 let (args_to_check, call_kind) = match *call_or_other {
501                     Call(_, ref args) => (&args[..], "function"),
502                     // first "argument" is self (which sometimes needs parens)
503                     MethodCall(_, ref args) => (&args[1..], "method"),
504                     // actual catch-all arm
505                     _ => {
506                         return;
507                     }
508                 };
509                 // Don't lint if this is a nested macro expansion: otherwise, the lint could
510                 // trigger in situations that macro authors shouldn't have to care about, e.g.,
511                 // when a parenthesized token tree matched in one macro expansion is matched as
512                 // an expression in another and used as a fn/method argument (Issue #47775)
513                 if e.span.ctxt().outer_expn_data().call_site.from_expansion() {
514                     return;
515                 }
516                 let msg = format!("{} argument", call_kind);
517                 for arg in args_to_check {
518                     self.check_unused_parens_expr(cx, arg, &msg, false, None, None);
519                 }
520                 return;
521             }
522         };
523         self.check_unused_parens_expr(cx, &value, msg, followed_by_block, left_pos, right_pos);
524     }
525
526     fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
527         use ast::{Mutability, PatKind::*};
528         match &p.kind {
529             // Do not lint on `(..)` as that will result in the other arms being useless.
530             Paren(_)
531             // The other cases do not contain sub-patterns.
532             | Wild | Rest | Lit(..) | Mac(..) | Range(..) | Ident(.., None) | Path(..) => return,
533             // These are list-like patterns; parens can always be removed.
534             TupleStruct(_, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
535                 self.check_unused_parens_pat(cx, p, false, false);
536             },
537             Struct(_, fps, _) => for f in fps {
538                 self.check_unused_parens_pat(cx, &f.pat, false, false);
539             },
540             // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106.
541             Ident(.., Some(p)) | Box(p) => self.check_unused_parens_pat(cx, p, true, false),
542             // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342.
543             // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106.
544             Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not),
545         }
546     }
547
548     fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
549         if let ast::StmtKind::Local(ref local) = s.kind {
550             self.check_unused_parens_pat(cx, &local.pat, false, false);
551
552             if let Some(ref value) = local.init {
553                 self.check_unused_parens_expr(cx, &value, "assigned value", false, None, None);
554             }
555         }
556     }
557
558     fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) {
559         self.check_unused_parens_pat(cx, &param.pat, true, false);
560     }
561
562     fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
563         self.check_unused_parens_pat(cx, &arm.pat, false, false);
564     }
565
566     fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
567         if let &ast::TyKind::Paren(ref r) = &ty.kind {
568             match &r.kind {
569                 &ast::TyKind::TraitObject(..) => {}
570                 &ast::TyKind::ImplTrait(_, ref bounds) if bounds.len() > 1 => {}
571                 _ => {
572                     let pattern_text =
573                         if let Ok(snippet) = cx.sess().source_map().span_to_snippet(ty.span) {
574                             snippet
575                         } else {
576                             pprust::ty_to_string(ty)
577                         };
578
579                     Self::remove_outer_parens(cx, ty.span, &pattern_text, "type", (false, false));
580                 }
581             }
582         }
583     }
584 }
585
586 declare_lint! {
587     UNUSED_IMPORT_BRACES,
588     Allow,
589     "unnecessary braces around an imported item"
590 }
591
592 declare_lint_pass!(UnusedImportBraces => [UNUSED_IMPORT_BRACES]);
593
594 impl UnusedImportBraces {
595     fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) {
596         if let ast::UseTreeKind::Nested(ref items) = use_tree.kind {
597             // Recursively check nested UseTrees
598             for &(ref tree, _) in items {
599                 self.check_use_tree(cx, tree, item);
600             }
601
602             // Trigger the lint only if there is one nested item
603             if items.len() != 1 {
604                 return;
605             }
606
607             // Trigger the lint if the nested item is a non-self single item
608             let node_name = match items[0].0.kind {
609                 ast::UseTreeKind::Simple(rename, ..) => {
610                     let orig_ident = items[0].0.prefix.segments.last().unwrap().ident;
611                     if orig_ident.name == kw::SelfLower {
612                         return;
613                     }
614                     rename.unwrap_or(orig_ident).name
615                 }
616                 ast::UseTreeKind::Glob => Symbol::intern("*"),
617                 ast::UseTreeKind::Nested(_) => return,
618             };
619
620             let msg = format!("braces around {} is unnecessary", node_name);
621             cx.span_lint(UNUSED_IMPORT_BRACES, item.span, &msg);
622         }
623     }
624 }
625
626 impl EarlyLintPass for UnusedImportBraces {
627     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
628         if let ast::ItemKind::Use(ref use_tree) = item.kind {
629             self.check_use_tree(cx, use_tree, item);
630         }
631     }
632 }
633
634 declare_lint! {
635     pub(super) UNUSED_ALLOCATION,
636     Warn,
637     "detects unnecessary allocations that can be eliminated"
638 }
639
640 declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]);
641
642 impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedAllocation {
643     fn check_expr(&mut self, cx: &LateContext<'_, '_>, e: &hir::Expr<'_>) {
644         match e.kind {
645             hir::ExprKind::Box(_) => {}
646             _ => return,
647         }
648
649         for adj in cx.tables.expr_adjustments(e) {
650             if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind {
651                 let msg = match m {
652                     adjustment::AutoBorrowMutability::Not => {
653                         "unnecessary allocation, use `&` instead"
654                     }
655                     adjustment::AutoBorrowMutability::Mut { .. } => {
656                         "unnecessary allocation, use `&mut` instead"
657                     }
658                 };
659                 cx.span_lint(UNUSED_ALLOCATION, e.span, msg);
660             }
661         }
662     }
663 }