1use std::iter;
2
3use rustc_ast::util::{classify, parser};
4use rustc_ast::{self as ast, ExprKind, HasAttrs as _, StmtKind};
5use rustc_attr_data_structures::{AttributeKind, find_attr};
6use rustc_data_structures::fx::FxHashMap;
7use rustc_errors::{MultiSpan, pluralize};
8use rustc_hir::def::{DefKind, Res};
9use rustc_hir::def_id::DefId;
10use rustc_hir::{self as hir, LangItem};
11use rustc_infer::traits::util::elaborate;
12use rustc_middle::ty::{self, Ty, adjustment};
13use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
14use rustc_span::edition::Edition::Edition2015;
15use rustc_span::{BytePos, Span, Symbol, kw, sym};
16use tracing::instrument;
17
18use crate::lints::{
19 PathStatementDrop, PathStatementDropSub, PathStatementNoEffect, UnusedAllocationDiag,
20 UnusedAllocationMutDiag, UnusedClosure, UnusedCoroutine, UnusedDef, UnusedDefSuggestion,
21 UnusedDelim, UnusedDelimSuggestion, UnusedImportBracesDiag, UnusedOp, UnusedOpSuggestion,
22 UnusedResult,
23};
24use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Lint, LintContext};
25
26declare_lint! {
27 pub UNUSED_MUST_USE,
51 Warn,
52 "unused result of a type flagged as `#[must_use]`",
53 report_in_external_macro
54}
55
56declare_lint! {
57 pub UNUSED_RESULTS,
91 Allow,
92 "unused result of an expression in a statement"
93}
94
95declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]);
96
97impl<'tcx> LateLintPass<'tcx> for UnusedResults {
98 fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
99 let hir::StmtKind::Semi(mut expr) = s.kind else {
100 return;
101 };
102
103 let mut expr_is_from_block = false;
104 while let hir::ExprKind::Block(blk, ..) = expr.kind
105 && let hir::Block { expr: Some(e), .. } = blk
106 {
107 expr = e;
108 expr_is_from_block = true;
109 }
110
111 if let hir::ExprKind::Ret(..) = expr.kind {
112 return;
113 }
114
115 if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind
116 && let ty = cx.typeck_results().expr_ty(await_expr)
117 && let ty::Alias(ty::Opaque, ty::AliasTy { def_id: future_def_id, .. }) = ty.kind()
118 && cx.tcx.ty_is_opaque_future(ty)
119 && let async_fn_def_id = cx.tcx.parent(*future_def_id)
120 && matches!(cx.tcx.def_kind(async_fn_def_id), DefKind::Fn | DefKind::AssocFn)
121 && cx.tcx.asyncness(async_fn_def_id).is_async()
123 && check_must_use_def(
124 cx,
125 async_fn_def_id,
126 expr.span,
127 "output of future returned by ",
128 "",
129 expr_is_from_block,
130 )
131 {
132 return;
135 }
136
137 let ty = cx.typeck_results().expr_ty(expr);
138
139 let must_use_result = is_ty_must_use(cx, ty, expr, expr.span);
140 let type_lint_emitted_or_suppressed = match must_use_result {
141 Some(path) => {
142 emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block);
143 true
144 }
145 None => false,
146 };
147
148 let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block);
149
150 if !fn_warned && type_lint_emitted_or_suppressed {
151 return;
154 }
155
156 let must_use_op = match expr.kind {
157 hir::ExprKind::Binary(bin_op, ..) => match bin_op.node {
161 hir::BinOpKind::Eq
162 | hir::BinOpKind::Lt
163 | hir::BinOpKind::Le
164 | hir::BinOpKind::Ne
165 | hir::BinOpKind::Ge
166 | hir::BinOpKind::Gt => Some("comparison"),
167 hir::BinOpKind::Add
168 | hir::BinOpKind::Sub
169 | hir::BinOpKind::Div
170 | hir::BinOpKind::Mul
171 | hir::BinOpKind::Rem => Some("arithmetic operation"),
172 hir::BinOpKind::And | hir::BinOpKind::Or => Some("logical operation"),
173 hir::BinOpKind::BitXor
174 | hir::BinOpKind::BitAnd
175 | hir::BinOpKind::BitOr
176 | hir::BinOpKind::Shl
177 | hir::BinOpKind::Shr => Some("bitwise operation"),
178 },
179 hir::ExprKind::AddrOf(..) => Some("borrow"),
180 hir::ExprKind::OffsetOf(..) => Some("`offset_of` call"),
181 hir::ExprKind::Unary(..) => Some("unary operation"),
182 _ => None,
183 };
184
185 let mut op_warned = false;
186
187 if let Some(must_use_op) = must_use_op {
188 let span = expr.span.find_oldest_ancestor_in_same_ctxt();
189 cx.emit_span_lint(
190 UNUSED_MUST_USE,
191 expr.span,
192 UnusedOp {
193 op: must_use_op,
194 label: expr.span,
195 suggestion: if expr_is_from_block {
196 UnusedOpSuggestion::BlockTailExpr {
197 before_span: span.shrink_to_lo(),
198 after_span: span.shrink_to_hi(),
199 }
200 } else {
201 UnusedOpSuggestion::NormalExpr { span: span.shrink_to_lo() }
202 },
203 },
204 );
205 op_warned = true;
206 }
207
208 if !(type_lint_emitted_or_suppressed || fn_warned || op_warned) {
209 cx.emit_span_lint(UNUSED_RESULTS, s.span, UnusedResult { ty });
210 }
211
212 fn check_fn_must_use(
213 cx: &LateContext<'_>,
214 expr: &hir::Expr<'_>,
215 expr_is_from_block: bool,
216 ) -> bool {
217 let maybe_def_id = match expr.kind {
218 hir::ExprKind::Call(callee, _) => {
219 match callee.kind {
220 hir::ExprKind::Path(ref qpath) => {
221 match cx.qpath_res(qpath, callee.hir_id) {
222 Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => Some(def_id),
223 _ => None,
226 }
227 }
228 _ => None,
229 }
230 }
231 hir::ExprKind::MethodCall(..) => {
232 cx.typeck_results().type_dependent_def_id(expr.hir_id)
233 }
234 _ => None,
235 };
236 if let Some(def_id) = maybe_def_id {
237 check_must_use_def(
238 cx,
239 def_id,
240 expr.span,
241 "return value of ",
242 "",
243 expr_is_from_block,
244 )
245 } else {
246 false
247 }
248 }
249
250 #[derive(Debug)]
252 enum MustUsePath {
253 Suppressed,
255 Def(Span, DefId, Option<Symbol>),
257 Boxed(Box<Self>),
258 Pinned(Box<Self>),
259 Opaque(Box<Self>),
260 TraitObject(Box<Self>),
261 TupleElement(Vec<(usize, Self)>),
262 Array(Box<Self>, u64),
263 Closure(Span),
265 Coroutine(Span),
267 }
268
269 #[instrument(skip(cx, expr), level = "debug", ret)]
270 fn is_ty_must_use<'tcx>(
271 cx: &LateContext<'tcx>,
272 ty: Ty<'tcx>,
273 expr: &hir::Expr<'_>,
274 span: Span,
275 ) -> Option<MustUsePath> {
276 if ty.is_unit()
277 || !ty.is_inhabited_from(
278 cx.tcx,
279 cx.tcx.parent_module(expr.hir_id).to_def_id(),
280 cx.typing_env(),
281 )
282 {
283 return Some(MustUsePath::Suppressed);
284 }
285
286 match *ty.kind() {
287 ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => {
288 is_ty_must_use(cx, boxed, expr, span)
289 .map(|inner| MustUsePath::Boxed(Box::new(inner)))
290 }
291 ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
292 let pinned_ty = args.type_at(0);
293 is_ty_must_use(cx, pinned_ty, expr, span)
294 .map(|inner| MustUsePath::Pinned(Box::new(inner)))
295 }
296 ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
297 ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => {
298 elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied())
299 .filter_only_self()
301 .find_map(|(pred, _span)| {
302 if let ty::ClauseKind::Trait(ref poly_trait_predicate) =
304 pred.kind().skip_binder()
305 {
306 let def_id = poly_trait_predicate.trait_ref.def_id;
307
308 is_def_must_use(cx, def_id, span)
309 } else {
310 None
311 }
312 })
313 .map(|inner| MustUsePath::Opaque(Box::new(inner)))
314 }
315 ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| {
316 if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
317 {
318 let def_id = trait_ref.def_id;
319 is_def_must_use(cx, def_id, span)
320 .map(|inner| MustUsePath::TraitObject(Box::new(inner)))
321 } else {
322 None
323 }
324 }),
325 ty::Tuple(tys) => {
326 let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind {
327 debug_assert_eq!(elem_exprs.len(), tys.len());
328 elem_exprs
329 } else {
330 &[]
331 };
332
333 let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr));
335
336 let nested_must_use = tys
337 .iter()
338 .zip(elem_exprs)
339 .enumerate()
340 .filter_map(|(i, (ty, expr))| {
341 is_ty_must_use(cx, ty, expr, expr.span).map(|path| (i, path))
342 })
343 .collect::<Vec<_>>();
344
345 if !nested_must_use.is_empty() {
346 Some(MustUsePath::TupleElement(nested_must_use))
347 } else {
348 None
349 }
350 }
351 ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) {
352 Some(0) | None => None,
354 Some(len) => is_ty_must_use(cx, ty, expr, span)
356 .map(|inner| MustUsePath::Array(Box::new(inner), len)),
357 },
358 ty::Closure(..) | ty::CoroutineClosure(..) => Some(MustUsePath::Closure(span)),
359 ty::Coroutine(def_id, ..) => {
360 let must_use = if cx.tcx.coroutine_is_async(def_id) {
362 let def_id = cx.tcx.lang_items().future_trait()?;
363 is_def_must_use(cx, def_id, span)
364 .map(|inner| MustUsePath::Opaque(Box::new(inner)))
365 } else {
366 None
367 };
368 must_use.or(Some(MustUsePath::Coroutine(span)))
369 }
370 _ => None,
371 }
372 }
373
374 fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option<MustUsePath> {
375 if let Some(reason) = find_attr!(
376 cx.tcx.get_all_attrs(def_id),
377 AttributeKind::MustUse { reason, .. } => reason
378 ) {
379 Some(MustUsePath::Def(span, def_id, *reason))
381 } else {
382 None
383 }
384 }
385
386 fn check_must_use_def(
389 cx: &LateContext<'_>,
390 def_id: DefId,
391 span: Span,
392 descr_pre_path: &str,
393 descr_post_path: &str,
394 expr_is_from_block: bool,
395 ) -> bool {
396 is_def_must_use(cx, def_id, span)
397 .map(|must_use_path| {
398 emit_must_use_untranslated(
399 cx,
400 &must_use_path,
401 descr_pre_path,
402 descr_post_path,
403 1,
404 false,
405 expr_is_from_block,
406 )
407 })
408 .is_some()
409 }
410
411 #[instrument(skip(cx), level = "debug")]
412 fn emit_must_use_untranslated(
413 cx: &LateContext<'_>,
414 path: &MustUsePath,
415 descr_pre: &str,
416 descr_post: &str,
417 plural_len: usize,
418 is_inner: bool,
419 expr_is_from_block: bool,
420 ) {
421 let plural_suffix = pluralize!(plural_len);
422
423 match path {
424 MustUsePath::Suppressed => {}
425 MustUsePath::Boxed(path) => {
426 let descr_pre = &format!("{descr_pre}boxed ");
427 emit_must_use_untranslated(
428 cx,
429 path,
430 descr_pre,
431 descr_post,
432 plural_len,
433 true,
434 expr_is_from_block,
435 );
436 }
437 MustUsePath::Pinned(path) => {
438 let descr_pre = &format!("{descr_pre}pinned ");
439 emit_must_use_untranslated(
440 cx,
441 path,
442 descr_pre,
443 descr_post,
444 plural_len,
445 true,
446 expr_is_from_block,
447 );
448 }
449 MustUsePath::Opaque(path) => {
450 let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of ");
451 emit_must_use_untranslated(
452 cx,
453 path,
454 descr_pre,
455 descr_post,
456 plural_len,
457 true,
458 expr_is_from_block,
459 );
460 }
461 MustUsePath::TraitObject(path) => {
462 let descr_post = &format!(" trait object{plural_suffix}{descr_post}");
463 emit_must_use_untranslated(
464 cx,
465 path,
466 descr_pre,
467 descr_post,
468 plural_len,
469 true,
470 expr_is_from_block,
471 );
472 }
473 MustUsePath::TupleElement(elems) => {
474 for (index, path) in elems {
475 let descr_post = &format!(" in tuple element {index}");
476 emit_must_use_untranslated(
477 cx,
478 path,
479 descr_pre,
480 descr_post,
481 plural_len,
482 true,
483 expr_is_from_block,
484 );
485 }
486 }
487 MustUsePath::Array(path, len) => {
488 let descr_pre = &format!("{descr_pre}array{plural_suffix} of ");
489 emit_must_use_untranslated(
490 cx,
491 path,
492 descr_pre,
493 descr_post,
494 plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)),
495 true,
496 expr_is_from_block,
497 );
498 }
499 MustUsePath::Closure(span) => {
500 cx.emit_span_lint(
501 UNUSED_MUST_USE,
502 *span,
503 UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post },
504 );
505 }
506 MustUsePath::Coroutine(span) => {
507 cx.emit_span_lint(
508 UNUSED_MUST_USE,
509 *span,
510 UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post },
511 );
512 }
513 MustUsePath::Def(span, def_id, reason) => {
514 let span = span.find_oldest_ancestor_in_same_ctxt();
515 cx.emit_span_lint(
516 UNUSED_MUST_USE,
517 span,
518 UnusedDef {
519 pre: descr_pre,
520 post: descr_post,
521 cx,
522 def_id: *def_id,
523 note: *reason,
524 suggestion: (!is_inner).then_some(if expr_is_from_block {
525 UnusedDefSuggestion::BlockTailExpr {
526 before_span: span.shrink_to_lo(),
527 after_span: span.shrink_to_hi(),
528 }
529 } else {
530 UnusedDefSuggestion::NormalExpr { span: span.shrink_to_lo() }
531 }),
532 },
533 );
534 }
535 }
536 }
537 }
538}
539
540declare_lint! {
541 pub PATH_STATEMENTS,
557 Warn,
558 "path statements with no effect"
559}
560
561declare_lint_pass!(PathStatements => [PATH_STATEMENTS]);
562
563impl<'tcx> LateLintPass<'tcx> for PathStatements {
564 fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
565 if let hir::StmtKind::Semi(expr) = s.kind {
566 if let hir::ExprKind::Path(_) = expr.kind {
567 let ty = cx.typeck_results().expr_ty(expr);
568 if ty.needs_drop(cx.tcx, cx.typing_env()) {
569 let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span)
570 {
571 PathStatementDropSub::Suggestion { span: s.span, snippet }
572 } else {
573 PathStatementDropSub::Help { span: s.span }
574 };
575 cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub })
576 } else {
577 cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect);
578 }
579 }
580 }
581 }
582}
583
584#[derive(Copy, Clone, Debug, PartialEq, Eq)]
585enum UnusedDelimsCtx {
586 FunctionArg,
587 MethodArg,
588 AssignedValue,
589 AssignedValueLetElse,
590 IfCond,
591 WhileCond,
592 ForIterExpr,
593 MatchScrutineeExpr,
594 ReturnValue,
595 BlockRetValue,
596 BreakValue,
597 LetScrutineeExpr,
598 ArrayLenExpr,
599 AnonConst,
600 MatchArmExpr,
601 IndexExpr,
602}
603
604impl From<UnusedDelimsCtx> for &'static str {
605 fn from(ctx: UnusedDelimsCtx) -> &'static str {
606 match ctx {
607 UnusedDelimsCtx::FunctionArg => "function argument",
608 UnusedDelimsCtx::MethodArg => "method argument",
609 UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => {
610 "assigned value"
611 }
612 UnusedDelimsCtx::IfCond => "`if` condition",
613 UnusedDelimsCtx::WhileCond => "`while` condition",
614 UnusedDelimsCtx::ForIterExpr => "`for` iterator expression",
615 UnusedDelimsCtx::MatchScrutineeExpr => "`match` scrutinee expression",
616 UnusedDelimsCtx::ReturnValue => "`return` value",
617 UnusedDelimsCtx::BlockRetValue => "block return value",
618 UnusedDelimsCtx::BreakValue => "`break` value",
619 UnusedDelimsCtx::LetScrutineeExpr => "`let` scrutinee expression",
620 UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression",
621 UnusedDelimsCtx::MatchArmExpr => "match arm expression",
622 UnusedDelimsCtx::IndexExpr => "index expression",
623 }
624 }
625}
626
627trait UnusedDelimLint {
629 const DELIM_STR: &'static str;
630
631 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool;
643
644 fn lint(&self) -> &'static Lint;
646
647 fn check_unused_delims_expr(
648 &self,
649 cx: &EarlyContext<'_>,
650 value: &ast::Expr,
651 ctx: UnusedDelimsCtx,
652 followed_by_block: bool,
653 left_pos: Option<BytePos>,
654 right_pos: Option<BytePos>,
655 is_kw: bool,
656 );
657
658 fn is_expr_delims_necessary(
659 inner: &ast::Expr,
660 ctx: UnusedDelimsCtx,
661 followed_by_block: bool,
662 ) -> bool {
663 let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
664
665 if followed_by_else {
666 match inner.kind {
667 ast::ExprKind::Binary(op, ..) if op.node.is_lazy() => return true,
668 _ if classify::expr_trailing_brace(inner).is_some() => return true,
669 _ => {}
670 }
671 }
672
673 if let ast::ExprKind::Range(..) = inner.kind
675 && matches!(ctx, UnusedDelimsCtx::LetScrutineeExpr)
676 {
677 return true;
678 }
679
680 if matches!(inner.kind, ast::ExprKind::AddrOf(ast::BorrowKind::Raw, ..)) {
684 return true;
685 }
686
687 {
717 let mut innermost = inner;
718 loop {
719 innermost = match &innermost.kind {
720 ExprKind::Binary(_op, lhs, _rhs) => lhs,
721 ExprKind::Call(fn_, _params) => fn_,
722 ExprKind::Cast(expr, _ty) => expr,
723 ExprKind::Type(expr, _ty) => expr,
724 ExprKind::Index(base, _subscript, _) => base,
725 _ => break,
726 };
727 if !classify::expr_requires_semi_to_be_stmt(innermost) {
728 return true;
729 }
730 }
731 }
732
733 if !followed_by_block {
736 return false;
737 }
738
739 {
741 let mut innermost = inner;
742 loop {
743 innermost = match &innermost.kind {
744 ExprKind::AddrOf(_, _, expr) => expr,
745 _ => {
746 if parser::contains_exterior_struct_lit(innermost) {
747 return true;
748 } else {
749 break;
750 }
751 }
752 }
753 }
754 }
755
756 let mut innermost = inner;
757 loop {
758 innermost = match &innermost.kind {
759 ExprKind::Unary(_op, expr) => expr,
760 ExprKind::Binary(_op, _lhs, rhs) => rhs,
761 ExprKind::AssignOp(_op, _lhs, rhs) => rhs,
762 ExprKind::Assign(_lhs, rhs, _span) => rhs,
763
764 ExprKind::Ret(_) | ExprKind::Yield(..) | ExprKind::Yeet(..) => return true,
765
766 ExprKind::Break(_label, None) => return false,
767 ExprKind::Break(_label, Some(break_expr)) => {
768 return matches!(break_expr.kind, ExprKind::Block(..));
769 }
770
771 ExprKind::Range(_lhs, Some(rhs), _limits) => {
772 return matches!(rhs.kind, ExprKind::Block(..));
773 }
774
775 _ => return parser::contains_exterior_struct_lit(inner),
776 }
777 }
778 }
779
780 fn emit_unused_delims_expr(
781 &self,
782 cx: &EarlyContext<'_>,
783 value: &ast::Expr,
784 ctx: UnusedDelimsCtx,
785 left_pos: Option<BytePos>,
786 right_pos: Option<BytePos>,
787 is_kw: bool,
788 ) {
789 let span_with_attrs = match value.kind {
790 ast::ExprKind::Block(ref block, None) if let [stmt] = block.stmts.as_slice() => {
791 if let Some(attr_lo) = stmt.attrs().iter().map(|attr| attr.span.lo()).min() {
794 stmt.span.with_lo(attr_lo)
795 } else {
796 stmt.span
797 }
798 }
799 ast::ExprKind::Paren(ref expr) => {
800 if let Some(attr_lo) = expr.attrs.iter().map(|attr| attr.span.lo()).min() {
803 expr.span.with_lo(attr_lo)
804 } else {
805 expr.span
806 }
807 }
808 _ => return,
809 };
810 let spans = span_with_attrs
811 .find_ancestor_inside(value.span)
812 .map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi())));
813 let keep_space = (
814 left_pos.is_some_and(|s| s >= value.span.lo()),
815 right_pos.is_some_and(|s| s <= value.span.hi()),
816 );
817 self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space, is_kw);
818 }
819
820 fn emit_unused_delims(
821 &self,
822 cx: &EarlyContext<'_>,
823 value_span: Span,
824 spans: Option<(Span, Span)>,
825 msg: &str,
826 keep_space: (bool, bool),
827 is_kw: bool,
828 ) {
829 let primary_span = if let Some((lo, hi)) = spans {
830 if hi.is_empty() {
831 return;
833 }
834 MultiSpan::from(vec![lo, hi])
835 } else {
836 MultiSpan::from(value_span)
837 };
838 let suggestion = spans.map(|(lo, hi)| {
839 let sm = cx.sess().source_map();
840 let lo_replace = if (keep_space.0 || is_kw)
841 && let Ok(snip) = sm.span_to_prev_source(lo)
842 && !snip.ends_with(' ')
843 {
844 " "
845 } else {
846 ""
847 };
848
849 let hi_replace = if keep_space.1
850 && let Ok(snip) = sm.span_to_next_source(hi)
851 && !snip.starts_with(' ')
852 {
853 " "
854 } else {
855 ""
856 };
857 UnusedDelimSuggestion {
858 start_span: lo,
859 start_replace: lo_replace,
860 end_span: hi,
861 end_replace: hi_replace,
862 }
863 });
864 cx.emit_span_lint(
865 self.lint(),
866 primary_span,
867 UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion },
868 );
869 }
870
871 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
872 use rustc_ast::ExprKind::*;
873 let (value, ctx, followed_by_block, left_pos, right_pos, is_kw) = match e.kind {
874 If(ref cond, ref block, _)
876 if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
877 {
878 let left = e.span.lo() + rustc_span::BytePos(2);
879 let right = block.span.lo();
880 (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right), true)
881 }
882
883 While(ref cond, ref block, ..)
885 if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
886 {
887 let left = e.span.lo() + rustc_span::BytePos(5);
888 let right = block.span.lo();
889 (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right), true)
890 }
891
892 ForLoop { ref iter, ref body, .. } => {
893 (iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true)
894 }
895
896 Match(ref head, _, ast::MatchKind::Prefix)
897 if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
898 {
899 let left = e.span.lo() + rustc_span::BytePos(5);
900 (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true)
901 }
902
903 Ret(Some(ref value)) => {
904 let left = e.span.lo() + rustc_span::BytePos(3);
905 (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true)
906 }
907
908 Break(_, Some(ref value)) => {
909 (value, UnusedDelimsCtx::BreakValue, false, None, None, true)
910 }
911
912 Index(_, ref value, _) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false),
913
914 Assign(_, ref value, _) | AssignOp(.., ref value) => {
915 (value, UnusedDelimsCtx::AssignedValue, false, None, None, false)
916 }
917 ref call_or_other => {
919 let (args_to_check, ctx) = match *call_or_other {
920 Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg),
921 MethodCall(ref call) => (&call.args[..], UnusedDelimsCtx::MethodArg),
922 _ => {
924 return;
925 }
926 };
927 if e.span.ctxt().outer_expn_data().call_site.from_expansion() {
932 return;
933 }
934 for arg in args_to_check {
935 self.check_unused_delims_expr(cx, arg, ctx, false, None, None, false);
936 }
937 return;
938 }
939 };
940 self.check_unused_delims_expr(
941 cx,
942 value,
943 ctx,
944 followed_by_block,
945 left_pos,
946 right_pos,
947 is_kw,
948 );
949 }
950
951 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
952 match s.kind {
953 StmtKind::Let(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
954 if let Some((init, els)) = local.kind.init_else_opt() {
955 if els.is_some()
956 && let ExprKind::Paren(paren) = &init.kind
957 && !init.span.eq_ctxt(paren.span)
958 {
959 return;
970 }
971 let ctx = match els {
972 None => UnusedDelimsCtx::AssignedValue,
973 Some(_) => UnusedDelimsCtx::AssignedValueLetElse,
974 };
975 self.check_unused_delims_expr(cx, init, ctx, false, None, None, false);
976 }
977 }
978 StmtKind::Expr(ref expr) => {
979 self.check_unused_delims_expr(
980 cx,
981 expr,
982 UnusedDelimsCtx::BlockRetValue,
983 false,
984 None,
985 None,
986 false,
987 );
988 }
989 _ => {}
990 }
991 }
992
993 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
994 use ast::ItemKind::*;
995
996 if let Const(box ast::ConstItem { expr: Some(expr), .. })
997 | Static(box ast::StaticItem { expr: Some(expr), .. }) = &item.kind
998 {
999 self.check_unused_delims_expr(
1000 cx,
1001 expr,
1002 UnusedDelimsCtx::AssignedValue,
1003 false,
1004 None,
1005 None,
1006 false,
1007 );
1008 }
1009 }
1010}
1011
1012declare_lint! {
1013 pub(super) UNUSED_PARENS,
1029 Warn,
1030 "`if`, `match`, `while` and `return` do not need parentheses"
1031}
1032
1033#[derive(Default)]
1034pub(crate) struct UnusedParens {
1035 with_self_ty_parens: bool,
1036 parens_in_cast_in_lt: Vec<ast::NodeId>,
1039 in_no_bounds_pos: FxHashMap<ast::NodeId, NoBoundsException>,
1042}
1043
1044enum NoBoundsException {
1059 None,
1061 OneBound,
1064}
1065
1066impl_lint_pass!(UnusedParens => [UNUSED_PARENS]);
1067
1068impl UnusedDelimLint for UnusedParens {
1069 const DELIM_STR: &'static str = "parentheses";
1070
1071 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = true;
1072
1073 fn lint(&self) -> &'static Lint {
1074 UNUSED_PARENS
1075 }
1076
1077 fn check_unused_delims_expr(
1078 &self,
1079 cx: &EarlyContext<'_>,
1080 value: &ast::Expr,
1081 ctx: UnusedDelimsCtx,
1082 followed_by_block: bool,
1083 left_pos: Option<BytePos>,
1084 right_pos: Option<BytePos>,
1085 is_kw: bool,
1086 ) {
1087 match value.kind {
1088 ast::ExprKind::Paren(ref inner) => {
1089 if !Self::is_expr_delims_necessary(inner, ctx, followed_by_block)
1090 && value.attrs.is_empty()
1091 && !value.span.from_expansion()
1092 && (ctx != UnusedDelimsCtx::LetScrutineeExpr
1093 || !matches!(inner.kind, ast::ExprKind::Binary(
1094 rustc_span::source_map::Spanned { node, .. },
1095 _,
1096 _,
1097 ) if node.is_lazy()))
1098 && !((ctx == UnusedDelimsCtx::ReturnValue
1099 || ctx == UnusedDelimsCtx::BreakValue)
1100 && matches!(inner.kind, ast::ExprKind::Assign(_, _, _)))
1101 {
1102 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1103 }
1104 }
1105 ast::ExprKind::Let(_, ref expr, _, _) => {
1106 self.check_unused_delims_expr(
1107 cx,
1108 expr,
1109 UnusedDelimsCtx::LetScrutineeExpr,
1110 followed_by_block,
1111 None,
1112 None,
1113 false,
1114 );
1115 }
1116 _ => {}
1117 }
1118 }
1119}
1120
1121impl UnusedParens {
1122 fn check_unused_parens_pat(
1123 &self,
1124 cx: &EarlyContext<'_>,
1125 value: &ast::Pat,
1126 avoid_or: bool,
1127 avoid_mut: bool,
1128 keep_space: (bool, bool),
1129 ) {
1130 use ast::{BindingMode, PatKind};
1131
1132 if let PatKind::Paren(inner) = &value.kind {
1133 match inner.kind {
1134 PatKind::Range(..) => return,
1139 PatKind::Or(..) if avoid_or => return,
1141 PatKind::Ident(BindingMode::MUT, ..) if avoid_mut => {
1143 return;
1144 }
1145 _ => {}
1147 }
1148 let spans = if !value.span.from_expansion() {
1149 inner
1150 .span
1151 .find_ancestor_inside(value.span)
1152 .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi())))
1153 } else {
1154 None
1155 };
1156 self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false);
1157 }
1158 }
1159
1160 fn cast_followed_by_lt(&self, expr: &ast::Expr) -> Option<ast::NodeId> {
1161 if let ExprKind::Binary(op, lhs, _rhs) = &expr.kind
1162 && (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl)
1163 {
1164 let mut cur = lhs;
1165 while let ExprKind::Binary(_, _, rhs) = &cur.kind {
1166 cur = rhs;
1167 }
1168
1169 if let ExprKind::Cast(_, ty) = &cur.kind
1170 && let ast::TyKind::Paren(_) = &ty.kind
1171 {
1172 return Some(ty.id);
1173 }
1174 }
1175 None
1176 }
1177}
1178
1179impl EarlyLintPass for UnusedParens {
1180 #[inline]
1181 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1182 if let Some(ty_id) = self.cast_followed_by_lt(e) {
1183 self.parens_in_cast_in_lt.push(ty_id);
1184 }
1185
1186 match e.kind {
1187 ExprKind::Let(ref pat, _, _, _) | ExprKind::ForLoop { ref pat, .. } => {
1188 self.check_unused_parens_pat(cx, pat, false, false, (true, true));
1189 }
1190 ExprKind::If(ref cond, ref block, ref else_)
1194 if matches!(cond.peel_parens().kind, ExprKind::Let(..)) =>
1195 {
1196 self.check_unused_delims_expr(
1197 cx,
1198 cond.peel_parens(),
1199 UnusedDelimsCtx::LetScrutineeExpr,
1200 true,
1201 None,
1202 None,
1203 true,
1204 );
1205 for stmt in &block.stmts {
1206 <Self as UnusedDelimLint>::check_stmt(self, cx, stmt);
1207 }
1208 if let Some(e) = else_ {
1209 <Self as UnusedDelimLint>::check_expr(self, cx, e);
1210 }
1211 return;
1212 }
1213 ExprKind::Match(ref _expr, ref arm, _) => {
1214 for a in arm {
1215 if let Some(body) = &a.body {
1216 self.check_unused_delims_expr(
1217 cx,
1218 body,
1219 UnusedDelimsCtx::MatchArmExpr,
1220 false,
1221 None,
1222 None,
1223 true,
1224 );
1225 }
1226 }
1227 }
1228 _ => {}
1229 }
1230
1231 <Self as UnusedDelimLint>::check_expr(self, cx, e)
1232 }
1233
1234 fn check_expr_post(&mut self, _cx: &EarlyContext<'_>, e: &ast::Expr) {
1235 if let Some(ty_id) = self.cast_followed_by_lt(e) {
1236 let id = self
1237 .parens_in_cast_in_lt
1238 .pop()
1239 .expect("check_expr and check_expr_post must balance");
1240 assert_eq!(
1241 id, ty_id,
1242 "check_expr, check_ty, and check_expr_post are called, in that order, by the visitor"
1243 );
1244 }
1245 }
1246
1247 fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
1248 use ast::Mutability;
1249 use ast::PatKind::*;
1250 let keep_space = (false, false);
1251 match &p.kind {
1252 Paren(_)
1254 | Missing | Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None)
1256 | Path(..) | Err(_) => {},
1257 TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
1259 self.check_unused_parens_pat(cx, p, false, false, keep_space);
1260 },
1261 Struct(_, _, fps, _) => for f in fps {
1262 self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space);
1263 },
1264 Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
1266 Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
1269 }
1270 }
1271
1272 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1273 if let StmtKind::Let(ref local) = s.kind {
1274 self.check_unused_parens_pat(cx, &local.pat, true, false, (true, false));
1275 }
1276
1277 <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1278 }
1279
1280 fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) {
1281 self.check_unused_parens_pat(cx, ¶m.pat, true, false, (false, false));
1282 }
1283
1284 fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
1285 self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false));
1286 }
1287
1288 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1289 if let ast::TyKind::Paren(_) = ty.kind
1290 && Some(&ty.id) == self.parens_in_cast_in_lt.last()
1291 {
1292 return;
1293 }
1294 match &ty.kind {
1295 ast::TyKind::Array(_, len) => {
1296 self.check_unused_delims_expr(
1297 cx,
1298 &len.value,
1299 UnusedDelimsCtx::ArrayLenExpr,
1300 false,
1301 None,
1302 None,
1303 false,
1304 );
1305 }
1306 ast::TyKind::Paren(r) => {
1307 let unused_parens = match &r.kind {
1308 ast::TyKind::ImplTrait(_, bounds) | ast::TyKind::TraitObject(bounds, _) => {
1309 match self.in_no_bounds_pos.get(&ty.id) {
1310 Some(NoBoundsException::None) => false,
1311 Some(NoBoundsException::OneBound) => bounds.len() <= 1,
1312 None => true,
1313 }
1314 }
1315 ast::TyKind::FnPtr(b) => {
1316 !self.with_self_ty_parens || b.generic_params.is_empty()
1317 }
1318 _ => true,
1319 };
1320
1321 if unused_parens {
1322 let spans = (!ty.span.from_expansion())
1323 .then(|| {
1324 r.span
1325 .find_ancestor_inside(ty.span)
1326 .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
1327 })
1328 .flatten();
1329
1330 self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false);
1331 }
1332
1333 self.with_self_ty_parens = false;
1334 }
1335 ast::TyKind::Ref(_, mut_ty) | ast::TyKind::Ptr(mut_ty) => {
1336 self.in_no_bounds_pos.insert(mut_ty.ty.id, NoBoundsException::OneBound);
1337 }
1338 ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
1339 for i in 0..bounds.len() {
1340 let is_last = i == bounds.len() - 1;
1341
1342 if let ast::GenericBound::Trait(poly_trait_ref) = &bounds[i] {
1343 let fn_with_explicit_ret_ty = if let [.., segment] =
1344 &*poly_trait_ref.trait_ref.path.segments
1345 && let Some(args) = segment.args.as_ref()
1346 && let ast::GenericArgs::Parenthesized(paren_args) = &**args
1347 && let ast::FnRetTy::Ty(ret_ty) = &paren_args.output
1348 {
1349 self.in_no_bounds_pos.insert(
1350 ret_ty.id,
1351 if is_last {
1352 NoBoundsException::OneBound
1353 } else {
1354 NoBoundsException::None
1355 },
1356 );
1357
1358 true
1359 } else {
1360 false
1361 };
1362
1363 let dyn2015_exception = cx.sess().psess.edition == Edition2015
1368 && matches!(ty.kind, ast::TyKind::TraitObject(..))
1369 && i == 0
1370 && poly_trait_ref
1371 .trait_ref
1372 .path
1373 .segments
1374 .first()
1375 .map(|s| s.ident.name == kw::PathRoot)
1376 .unwrap_or(false);
1377
1378 if let ast::Parens::Yes = poly_trait_ref.parens
1379 && (is_last || !fn_with_explicit_ret_ty)
1380 && !dyn2015_exception
1381 {
1382 let s = poly_trait_ref.span;
1383 let spans = (!s.from_expansion()).then(|| {
1384 (
1385 s.with_hi(s.lo() + rustc_span::BytePos(1)),
1386 s.with_lo(s.hi() - rustc_span::BytePos(1)),
1387 )
1388 });
1389
1390 self.emit_unused_delims(
1391 cx,
1392 poly_trait_ref.span,
1393 spans,
1394 "type",
1395 (false, false),
1396 false,
1397 );
1398 }
1399 }
1400 }
1401 }
1402 _ => {}
1403 }
1404 }
1405
1406 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1407 <Self as UnusedDelimLint>::check_item(self, cx, item)
1408 }
1409
1410 fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Item) {
1411 self.in_no_bounds_pos.clear();
1412 }
1413
1414 fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) {
1415 use rustc_ast::{WhereBoundPredicate, WherePredicateKind};
1416 if let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
1417 bounded_ty,
1418 bound_generic_params,
1419 ..
1420 }) = &pred.kind
1421 && let ast::TyKind::Paren(_) = &bounded_ty.kind
1422 && bound_generic_params.is_empty()
1423 {
1424 self.with_self_ty_parens = true;
1425 }
1426 }
1427
1428 fn exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate) {
1429 assert!(!self.with_self_ty_parens);
1430 }
1431}
1432
1433declare_lint! {
1434 pub(super) UNUSED_BRACES,
1452 Warn,
1453 "unnecessary braces around an expression"
1454}
1455
1456declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]);
1457
1458impl UnusedDelimLint for UnusedBraces {
1459 const DELIM_STR: &'static str = "braces";
1460
1461 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = false;
1462
1463 fn lint(&self) -> &'static Lint {
1464 UNUSED_BRACES
1465 }
1466
1467 fn check_unused_delims_expr(
1468 &self,
1469 cx: &EarlyContext<'_>,
1470 value: &ast::Expr,
1471 ctx: UnusedDelimsCtx,
1472 followed_by_block: bool,
1473 left_pos: Option<BytePos>,
1474 right_pos: Option<BytePos>,
1475 is_kw: bool,
1476 ) {
1477 match value.kind {
1478 ast::ExprKind::Block(ref inner, None)
1479 if inner.rules == ast::BlockCheckMode::Default =>
1480 {
1481 if let [stmt] = inner.stmts.as_slice() {
1506 if let ast::StmtKind::Expr(ref expr) = stmt.kind {
1507 if !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
1508 && (ctx != UnusedDelimsCtx::AnonConst
1509 || (matches!(expr.kind, ast::ExprKind::Lit(_))
1510 && !expr.span.from_expansion()))
1511 && !cx.sess().source_map().is_multiline(value.span)
1512 && value.attrs.is_empty()
1513 && !value.span.from_expansion()
1514 && !inner.span.from_expansion()
1515 {
1516 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1517 }
1518 }
1519 }
1520 }
1521 ast::ExprKind::Let(_, ref expr, _, _) => {
1522 self.check_unused_delims_expr(
1523 cx,
1524 expr,
1525 UnusedDelimsCtx::LetScrutineeExpr,
1526 followed_by_block,
1527 None,
1528 None,
1529 false,
1530 );
1531 }
1532 _ => {}
1533 }
1534 }
1535}
1536
1537impl EarlyLintPass for UnusedBraces {
1538 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1539 <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1540 }
1541
1542 #[inline]
1543 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1544 <Self as UnusedDelimLint>::check_expr(self, cx, e);
1545
1546 if let ExprKind::Repeat(_, ref anon_const) = e.kind {
1547 self.check_unused_delims_expr(
1548 cx,
1549 &anon_const.value,
1550 UnusedDelimsCtx::AnonConst,
1551 false,
1552 None,
1553 None,
1554 false,
1555 );
1556 }
1557 }
1558
1559 fn check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg) {
1560 if let ast::GenericArg::Const(ct) = arg {
1561 self.check_unused_delims_expr(
1562 cx,
1563 &ct.value,
1564 UnusedDelimsCtx::AnonConst,
1565 false,
1566 None,
1567 None,
1568 false,
1569 );
1570 }
1571 }
1572
1573 fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &ast::Variant) {
1574 if let Some(anon_const) = &v.disr_expr {
1575 self.check_unused_delims_expr(
1576 cx,
1577 &anon_const.value,
1578 UnusedDelimsCtx::AnonConst,
1579 false,
1580 None,
1581 None,
1582 false,
1583 );
1584 }
1585 }
1586
1587 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1588 match ty.kind {
1589 ast::TyKind::Array(_, ref len) => {
1590 self.check_unused_delims_expr(
1591 cx,
1592 &len.value,
1593 UnusedDelimsCtx::ArrayLenExpr,
1594 false,
1595 None,
1596 None,
1597 false,
1598 );
1599 }
1600
1601 ast::TyKind::Typeof(ref anon_const) => {
1602 self.check_unused_delims_expr(
1603 cx,
1604 &anon_const.value,
1605 UnusedDelimsCtx::AnonConst,
1606 false,
1607 None,
1608 None,
1609 false,
1610 );
1611 }
1612
1613 _ => {}
1614 }
1615 }
1616
1617 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1618 <Self as UnusedDelimLint>::check_item(self, cx, item)
1619 }
1620}
1621
1622declare_lint! {
1623 UNUSED_IMPORT_BRACES,
1648 Allow,
1649 "unnecessary braces around an imported item"
1650}
1651
1652declare_lint_pass!(UnusedImportBraces => [UNUSED_IMPORT_BRACES]);
1653
1654impl UnusedImportBraces {
1655 fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) {
1656 if let ast::UseTreeKind::Nested { ref items, .. } = use_tree.kind {
1657 for (tree, _) in items {
1659 self.check_use_tree(cx, tree, item);
1660 }
1661
1662 let [(tree, _)] = items.as_slice() else { return };
1664
1665 let node_name = match tree.kind {
1667 ast::UseTreeKind::Simple(rename) => {
1668 let orig_ident = tree.prefix.segments.last().unwrap().ident;
1669 if orig_ident.name == kw::SelfLower {
1670 return;
1671 }
1672 rename.unwrap_or(orig_ident).name
1673 }
1674 ast::UseTreeKind::Glob => sym::asterisk,
1675 ast::UseTreeKind::Nested { .. } => return,
1676 };
1677
1678 cx.emit_span_lint(
1679 UNUSED_IMPORT_BRACES,
1680 item.span,
1681 UnusedImportBracesDiag { node: node_name },
1682 );
1683 }
1684 }
1685}
1686
1687impl EarlyLintPass for UnusedImportBraces {
1688 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1689 if let ast::ItemKind::Use(ref use_tree) = item.kind {
1690 self.check_use_tree(cx, use_tree, item);
1691 }
1692 }
1693}
1694
1695declare_lint! {
1696 pub(super) UNUSED_ALLOCATION,
1715 Warn,
1716 "detects unnecessary allocations that can be eliminated"
1717}
1718
1719declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]);
1720
1721impl<'tcx> LateLintPass<'tcx> for UnusedAllocation {
1722 fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
1723 match e.kind {
1724 hir::ExprKind::Call(path_expr, [_])
1725 if let hir::ExprKind::Path(qpath) = &path_expr.kind
1726 && let Some(did) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()
1727 && cx.tcx.is_diagnostic_item(sym::box_new, did) => {}
1728 _ => return,
1729 }
1730
1731 for adj in cx.typeck_results().expr_adjustments(e) {
1732 if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(m)) = adj.kind {
1733 match m {
1734 adjustment::AutoBorrowMutability::Not => {
1735 cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag);
1736 }
1737 adjustment::AutoBorrowMutability::Mut { .. } => {
1738 cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag);
1739 }
1740 };
1741 }
1742 }
1743 }
1744}