1use std::iter;
2
3use rustc_ast::util::{classify, parser};
4use rustc_ast::{self as ast, ExprKind, FnRetTy, HasAttrs as _, StmtKind};
5use rustc_data_structures::fx::FxHashMap;
6use rustc_errors::{MultiSpan, pluralize};
7use rustc_hir::attrs::AttributeKind;
8use rustc_hir::def::{DefKind, Res};
9use rustc_hir::def_id::DefId;
10use rustc_hir::{self as hir, LangItem, find_attr};
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_ancestor_not_from_macro().unwrap_or(expr.span);
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_ancestor_not_from_macro().unwrap_or(*span);
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 && let hir::ExprKind::Path(_) = expr.kind
567 {
568 let ty = cx.typeck_results().expr_ty(expr);
569 if ty.needs_drop(cx.tcx, cx.typing_env()) {
570 let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) {
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#[derive(Copy, Clone, Debug, PartialEq, Eq)]
584enum UnusedDelimsCtx {
585 FunctionArg,
586 MethodArg,
587 AssignedValue,
588 AssignedValueLetElse,
589 IfCond,
590 WhileCond,
591 ForIterExpr,
592 MatchScrutineeExpr,
593 ReturnValue,
594 BlockRetValue,
595 BreakValue,
596 LetScrutineeExpr,
597 ArrayLenExpr,
598 AnonConst,
599 MatchArmExpr,
600 IndexExpr,
601 ClosureBody,
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 UnusedDelimsCtx::ClosureBody => "closure body",
624 }
625 }
626}
627
628trait UnusedDelimLint {
630 const DELIM_STR: &'static str;
631
632 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool;
644
645 fn lint(&self) -> &'static Lint;
647
648 fn check_unused_delims_expr(
649 &self,
650 cx: &EarlyContext<'_>,
651 value: &ast::Expr,
652 ctx: UnusedDelimsCtx,
653 followed_by_block: bool,
654 left_pos: Option<BytePos>,
655 right_pos: Option<BytePos>,
656 is_kw: bool,
657 );
658
659 fn is_expr_delims_necessary(
660 inner: &ast::Expr,
661 ctx: UnusedDelimsCtx,
662 followed_by_block: bool,
663 ) -> bool {
664 let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
665
666 if followed_by_else {
667 match inner.kind {
668 ast::ExprKind::Binary(op, ..) if op.node.is_lazy() => return true,
669 _ if classify::expr_trailing_brace(inner).is_some() => return true,
670 _ => {}
671 }
672 }
673
674 if let ast::ExprKind::Range(..) = inner.kind
676 && matches!(ctx, UnusedDelimsCtx::LetScrutineeExpr)
677 {
678 return true;
679 }
680
681 if matches!(inner.kind, ast::ExprKind::AddrOf(ast::BorrowKind::Raw, ..)) {
685 return true;
686 }
687
688 {
718 let mut innermost = inner;
719 loop {
720 innermost = match &innermost.kind {
721 ExprKind::Binary(_op, lhs, _rhs) => lhs,
722 ExprKind::Call(fn_, _params) => fn_,
723 ExprKind::Cast(expr, _ty) => expr,
724 ExprKind::Type(expr, _ty) => expr,
725 ExprKind::Index(base, _subscript, _) => base,
726 _ => break,
727 };
728 if !classify::expr_requires_semi_to_be_stmt(innermost) {
729 return true;
730 }
731 }
732 }
733
734 if !followed_by_block {
737 return false;
738 }
739
740 {
742 let mut innermost = inner;
743 loop {
744 innermost = match &innermost.kind {
745 ExprKind::AddrOf(_, _, expr) => expr,
746 _ => {
747 if parser::contains_exterior_struct_lit(innermost) {
748 return true;
749 } else {
750 break;
751 }
752 }
753 }
754 }
755 }
756
757 let mut innermost = inner;
758 loop {
759 innermost = match &innermost.kind {
760 ExprKind::Unary(_op, expr) => expr,
761 ExprKind::Binary(_op, _lhs, rhs) => rhs,
762 ExprKind::AssignOp(_op, _lhs, rhs) => rhs,
763 ExprKind::Assign(_lhs, rhs, _span) => rhs,
764
765 ExprKind::Ret(_) | ExprKind::Yield(..) | ExprKind::Yeet(..) => return true,
766
767 ExprKind::Break(_label, None) => return false,
768 ExprKind::Break(_label, Some(break_expr)) => {
769 return matches!(break_expr.kind, ExprKind::Block(..));
770 }
771
772 ExprKind::Range(_lhs, Some(rhs), _limits) => {
773 return matches!(rhs.kind, ExprKind::Block(..));
774 }
775
776 _ => return parser::contains_exterior_struct_lit(inner),
777 }
778 }
779 }
780
781 fn emit_unused_delims_expr(
782 &self,
783 cx: &EarlyContext<'_>,
784 value: &ast::Expr,
785 ctx: UnusedDelimsCtx,
786 left_pos: Option<BytePos>,
787 right_pos: Option<BytePos>,
788 is_kw: bool,
789 ) {
790 let span_with_attrs = match value.kind {
791 ast::ExprKind::Block(ref block, None) if let [stmt] = block.stmts.as_slice() => {
792 if let Some(attr_lo) = stmt.attrs().iter().map(|attr| attr.span.lo()).min() {
795 stmt.span.with_lo(attr_lo)
796 } else {
797 stmt.span
798 }
799 }
800 ast::ExprKind::Paren(ref expr) => {
801 if let Some(attr_lo) = expr.attrs.iter().map(|attr| attr.span.lo()).min() {
804 expr.span.with_lo(attr_lo)
805 } else {
806 expr.span
807 }
808 }
809 _ => return,
810 };
811 let spans = span_with_attrs
812 .find_ancestor_inside(value.span)
813 .map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi())));
814 let keep_space = (
815 left_pos.is_some_and(|s| s >= value.span.lo()),
816 right_pos.is_some_and(|s| s <= value.span.hi()),
817 );
818 self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space, is_kw);
819 }
820
821 fn emit_unused_delims(
822 &self,
823 cx: &EarlyContext<'_>,
824 value_span: Span,
825 spans: Option<(Span, Span)>,
826 msg: &str,
827 keep_space: (bool, bool),
828 is_kw: bool,
829 ) {
830 let primary_span = if let Some((lo, hi)) = spans {
831 if hi.is_empty() {
832 return;
834 }
835 MultiSpan::from(vec![lo, hi])
836 } else {
837 MultiSpan::from(value_span)
838 };
839 let suggestion = spans.map(|(lo, hi)| {
840 let sm = cx.sess().source_map();
841 let lo_replace = if (keep_space.0 || is_kw)
842 && let Ok(snip) = sm.span_to_prev_source(lo)
843 && !snip.ends_with(' ')
844 {
845 " "
846 } else {
847 ""
848 };
849
850 let hi_replace = if keep_space.1
851 && let Ok(snip) = sm.span_to_next_source(hi)
852 && !snip.starts_with(' ')
853 {
854 " "
855 } else {
856 ""
857 };
858 UnusedDelimSuggestion {
859 start_span: lo,
860 start_replace: lo_replace,
861 end_span: hi,
862 end_replace: hi_replace,
863 }
864 });
865 cx.emit_span_lint(
866 self.lint(),
867 primary_span,
868 UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion },
869 );
870 }
871
872 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
873 use rustc_ast::ExprKind::*;
874 let (value, ctx, followed_by_block, left_pos, right_pos, is_kw) = match e.kind {
875 If(ref cond, ref block, _)
877 if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
878 {
879 let left = e.span.lo() + rustc_span::BytePos(2);
880 let right = block.span.lo();
881 (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right), true)
882 }
883
884 While(ref cond, ref block, ..)
886 if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
887 {
888 let left = e.span.lo() + rustc_span::BytePos(5);
889 let right = block.span.lo();
890 (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right), true)
891 }
892
893 ForLoop { ref iter, ref body, .. } => {
894 (iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true)
895 }
896
897 Match(ref head, _, ast::MatchKind::Prefix)
898 if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
899 {
900 let left = e.span.lo() + rustc_span::BytePos(5);
901 (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true)
902 }
903
904 Ret(Some(ref value)) => {
905 let left = e.span.lo() + rustc_span::BytePos(3);
906 (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true)
907 }
908
909 Break(_, Some(ref value)) => {
910 (value, UnusedDelimsCtx::BreakValue, false, None, None, true)
911 }
912
913 Index(_, ref value, _) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false),
914
915 Assign(_, ref value, _) | AssignOp(.., ref value) => {
916 (value, UnusedDelimsCtx::AssignedValue, false, None, None, false)
917 }
918 ref call_or_other => {
920 let (args_to_check, ctx) = match *call_or_other {
921 Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg),
922 MethodCall(ref call) => (&call.args[..], UnusedDelimsCtx::MethodArg),
923 Closure(ref closure)
924 if matches!(closure.fn_decl.output, FnRetTy::Default(_)) =>
925 {
926 (&[closure.body.clone()][..], UnusedDelimsCtx::ClosureBody)
927 }
928 _ => {
930 return;
931 }
932 };
933 if e.span.ctxt().outer_expn_data().call_site.from_expansion() {
938 return;
939 }
940 for arg in args_to_check {
941 self.check_unused_delims_expr(cx, arg, ctx, false, None, None, false);
942 }
943 return;
944 }
945 };
946 self.check_unused_delims_expr(
947 cx,
948 value,
949 ctx,
950 followed_by_block,
951 left_pos,
952 right_pos,
953 is_kw,
954 );
955 }
956
957 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
958 match s.kind {
959 StmtKind::Let(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
960 if let Some((init, els)) = local.kind.init_else_opt() {
961 if els.is_some()
962 && let ExprKind::Paren(paren) = &init.kind
963 && !init.span.eq_ctxt(paren.span)
964 {
965 return;
976 }
977 let ctx = match els {
978 None => UnusedDelimsCtx::AssignedValue,
979 Some(_) => UnusedDelimsCtx::AssignedValueLetElse,
980 };
981 self.check_unused_delims_expr(cx, init, ctx, false, None, None, false);
982 }
983 }
984 StmtKind::Expr(ref expr) => {
985 self.check_unused_delims_expr(
986 cx,
987 expr,
988 UnusedDelimsCtx::BlockRetValue,
989 false,
990 None,
991 None,
992 false,
993 );
994 }
995 _ => {}
996 }
997 }
998
999 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1000 use ast::ItemKind::*;
1001
1002 if let Const(box ast::ConstItem { expr: Some(expr), .. })
1003 | Static(box ast::StaticItem { expr: Some(expr), .. }) = &item.kind
1004 {
1005 self.check_unused_delims_expr(
1006 cx,
1007 expr,
1008 UnusedDelimsCtx::AssignedValue,
1009 false,
1010 None,
1011 None,
1012 false,
1013 );
1014 }
1015 }
1016}
1017
1018declare_lint! {
1019 pub(super) UNUSED_PARENS,
1035 Warn,
1036 "`if`, `match`, `while` and `return` do not need parentheses"
1037}
1038
1039#[derive(Default)]
1040pub(crate) struct UnusedParens {
1041 with_self_ty_parens: bool,
1042 parens_in_cast_in_lt: Vec<ast::NodeId>,
1045 in_no_bounds_pos: FxHashMap<ast::NodeId, NoBoundsException>,
1048}
1049
1050enum NoBoundsException {
1065 None,
1067 OneBound,
1070}
1071
1072impl_lint_pass!(UnusedParens => [UNUSED_PARENS]);
1073
1074impl UnusedDelimLint for UnusedParens {
1075 const DELIM_STR: &'static str = "parentheses";
1076
1077 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = true;
1078
1079 fn lint(&self) -> &'static Lint {
1080 UNUSED_PARENS
1081 }
1082
1083 fn check_unused_delims_expr(
1084 &self,
1085 cx: &EarlyContext<'_>,
1086 value: &ast::Expr,
1087 ctx: UnusedDelimsCtx,
1088 followed_by_block: bool,
1089 left_pos: Option<BytePos>,
1090 right_pos: Option<BytePos>,
1091 is_kw: bool,
1092 ) {
1093 match value.kind {
1094 ast::ExprKind::Paren(ref inner) => {
1095 if !Self::is_expr_delims_necessary(inner, ctx, followed_by_block)
1096 && value.attrs.is_empty()
1097 && !value.span.from_expansion()
1098 && (ctx != UnusedDelimsCtx::LetScrutineeExpr
1099 || !matches!(inner.kind, ast::ExprKind::Binary(
1100 rustc_span::source_map::Spanned { node, .. },
1101 _,
1102 _,
1103 ) if node.is_lazy()))
1104 && !((ctx == UnusedDelimsCtx::ReturnValue
1105 || ctx == UnusedDelimsCtx::BreakValue)
1106 && matches!(inner.kind, ast::ExprKind::Assign(_, _, _)))
1107 {
1108 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1109 }
1110 }
1111 ast::ExprKind::Let(_, ref expr, _, _) => {
1112 self.check_unused_delims_expr(
1113 cx,
1114 expr,
1115 UnusedDelimsCtx::LetScrutineeExpr,
1116 followed_by_block,
1117 None,
1118 None,
1119 false,
1120 );
1121 }
1122 _ => {}
1123 }
1124 }
1125}
1126
1127impl UnusedParens {
1128 fn check_unused_parens_pat(
1129 &self,
1130 cx: &EarlyContext<'_>,
1131 value: &ast::Pat,
1132 avoid_or: bool,
1133 avoid_mut: bool,
1134 keep_space: (bool, bool),
1135 ) {
1136 use ast::{BindingMode, PatKind};
1137
1138 if let PatKind::Paren(inner) = &value.kind {
1139 match inner.kind {
1140 PatKind::Range(..) => return,
1145 PatKind::Or(..) if avoid_or => return,
1147 PatKind::Ident(BindingMode::MUT, ..) if avoid_mut => {
1149 return;
1150 }
1151 _ => {}
1153 }
1154 let spans = if !value.span.from_expansion() {
1155 inner
1156 .span
1157 .find_ancestor_inside(value.span)
1158 .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi())))
1159 } else {
1160 None
1161 };
1162 self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false);
1163 }
1164 }
1165
1166 fn cast_followed_by_lt(&self, expr: &ast::Expr) -> Option<ast::NodeId> {
1167 if let ExprKind::Binary(op, lhs, _rhs) = &expr.kind
1168 && (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl)
1169 {
1170 let mut cur = lhs;
1171 while let ExprKind::Binary(_, _, rhs) = &cur.kind {
1172 cur = rhs;
1173 }
1174
1175 if let ExprKind::Cast(_, ty) = &cur.kind
1176 && let ast::TyKind::Paren(_) = &ty.kind
1177 {
1178 return Some(ty.id);
1179 }
1180 }
1181 None
1182 }
1183}
1184
1185impl EarlyLintPass for UnusedParens {
1186 #[inline]
1187 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1188 if let Some(ty_id) = self.cast_followed_by_lt(e) {
1189 self.parens_in_cast_in_lt.push(ty_id);
1190 }
1191
1192 match e.kind {
1193 ExprKind::Let(ref pat, _, _, _) | ExprKind::ForLoop { ref pat, .. } => {
1194 self.check_unused_parens_pat(cx, pat, false, false, (true, true));
1195 }
1196 ExprKind::If(ref cond, ref block, ref else_)
1200 if matches!(cond.peel_parens().kind, ExprKind::Let(..)) =>
1201 {
1202 self.check_unused_delims_expr(
1203 cx,
1204 cond.peel_parens(),
1205 UnusedDelimsCtx::LetScrutineeExpr,
1206 true,
1207 None,
1208 None,
1209 true,
1210 );
1211 for stmt in &block.stmts {
1212 <Self as UnusedDelimLint>::check_stmt(self, cx, stmt);
1213 }
1214 if let Some(e) = else_ {
1215 <Self as UnusedDelimLint>::check_expr(self, cx, e);
1216 }
1217 return;
1218 }
1219 ExprKind::Match(ref _expr, ref arm, _) => {
1220 for a in arm {
1221 if let Some(body) = &a.body {
1222 self.check_unused_delims_expr(
1223 cx,
1224 body,
1225 UnusedDelimsCtx::MatchArmExpr,
1226 false,
1227 None,
1228 None,
1229 true,
1230 );
1231 }
1232 }
1233 }
1234 _ => {}
1235 }
1236
1237 <Self as UnusedDelimLint>::check_expr(self, cx, e)
1238 }
1239
1240 fn check_expr_post(&mut self, _cx: &EarlyContext<'_>, e: &ast::Expr) {
1241 if let Some(ty_id) = self.cast_followed_by_lt(e) {
1242 let id = self
1243 .parens_in_cast_in_lt
1244 .pop()
1245 .expect("check_expr and check_expr_post must balance");
1246 assert_eq!(
1247 id, ty_id,
1248 "check_expr, check_ty, and check_expr_post are called, in that order, by the visitor"
1249 );
1250 }
1251 }
1252
1253 fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
1254 use ast::Mutability;
1255 use ast::PatKind::*;
1256 let keep_space = (false, false);
1257 match &p.kind {
1258 Paren(_)
1260 | Missing | Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None)
1262 | Path(..) | Err(_) => {},
1263 TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
1265 self.check_unused_parens_pat(cx, p, false, false, keep_space);
1266 },
1267 Struct(_, _, fps, _) => for f in fps {
1268 self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space);
1269 },
1270 Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
1272 Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
1275 }
1276 }
1277
1278 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1279 if let StmtKind::Let(ref local) = s.kind {
1280 self.check_unused_parens_pat(cx, &local.pat, true, false, (true, false));
1281 }
1282
1283 <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1284 }
1285
1286 fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) {
1287 self.check_unused_parens_pat(cx, ¶m.pat, true, false, (false, false));
1288 }
1289
1290 fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
1291 self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false));
1292 }
1293
1294 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1295 if let ast::TyKind::Paren(_) = ty.kind
1296 && Some(&ty.id) == self.parens_in_cast_in_lt.last()
1297 {
1298 return;
1299 }
1300 match &ty.kind {
1301 ast::TyKind::Array(_, len) => {
1302 self.check_unused_delims_expr(
1303 cx,
1304 &len.value,
1305 UnusedDelimsCtx::ArrayLenExpr,
1306 false,
1307 None,
1308 None,
1309 false,
1310 );
1311 }
1312 ast::TyKind::Paren(r) => {
1313 let unused_parens = match &r.kind {
1314 ast::TyKind::ImplTrait(_, bounds) | ast::TyKind::TraitObject(bounds, _) => {
1315 match self.in_no_bounds_pos.get(&ty.id) {
1316 Some(NoBoundsException::None) => false,
1317 Some(NoBoundsException::OneBound) => bounds.len() <= 1,
1318 None => true,
1319 }
1320 }
1321 ast::TyKind::FnPtr(b) => {
1322 !self.with_self_ty_parens || b.generic_params.is_empty()
1323 }
1324 _ => true,
1325 };
1326
1327 if unused_parens {
1328 let spans = (!ty.span.from_expansion())
1329 .then(|| {
1330 r.span
1331 .find_ancestor_inside(ty.span)
1332 .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
1333 })
1334 .flatten();
1335
1336 self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false);
1337 }
1338
1339 self.with_self_ty_parens = false;
1340 }
1341 ast::TyKind::Ref(_, mut_ty) | ast::TyKind::Ptr(mut_ty) => {
1342 let own_constraint = self.in_no_bounds_pos.get(&ty.id);
1345 let constraint = match own_constraint {
1346 Some(NoBoundsException::None) => NoBoundsException::None,
1347 Some(NoBoundsException::OneBound) => NoBoundsException::OneBound,
1348 None => NoBoundsException::OneBound,
1349 };
1350 self.in_no_bounds_pos.insert(mut_ty.ty.id, constraint);
1351 }
1352 ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
1353 for i in 0..bounds.len() {
1354 let is_last = i == bounds.len() - 1;
1355
1356 if let ast::GenericBound::Trait(poly_trait_ref) = &bounds[i] {
1357 let fn_with_explicit_ret_ty = if let [.., segment] =
1358 &*poly_trait_ref.trait_ref.path.segments
1359 && let Some(args) = segment.args.as_ref()
1360 && let ast::GenericArgs::Parenthesized(paren_args) = &**args
1361 && let ast::FnRetTy::Ty(ret_ty) = &paren_args.output
1362 {
1363 self.in_no_bounds_pos.insert(
1364 ret_ty.id,
1365 if is_last {
1366 NoBoundsException::OneBound
1367 } else {
1368 NoBoundsException::None
1369 },
1370 );
1371
1372 true
1373 } else {
1374 false
1375 };
1376
1377 let dyn2015_exception = cx.sess().psess.edition == Edition2015
1382 && matches!(ty.kind, ast::TyKind::TraitObject(..))
1383 && i == 0
1384 && poly_trait_ref
1385 .trait_ref
1386 .path
1387 .segments
1388 .first()
1389 .map(|s| s.ident.name == kw::PathRoot)
1390 .unwrap_or(false);
1391
1392 if let ast::Parens::Yes = poly_trait_ref.parens
1393 && (is_last || !fn_with_explicit_ret_ty)
1394 && !dyn2015_exception
1395 {
1396 let s = poly_trait_ref.span;
1397 let spans = (!s.from_expansion()).then(|| {
1398 (
1399 s.with_hi(s.lo() + rustc_span::BytePos(1)),
1400 s.with_lo(s.hi() - rustc_span::BytePos(1)),
1401 )
1402 });
1403
1404 self.emit_unused_delims(
1405 cx,
1406 poly_trait_ref.span,
1407 spans,
1408 "type",
1409 (false, false),
1410 false,
1411 );
1412 }
1413 }
1414 }
1415 }
1416 _ => {}
1417 }
1418 }
1419
1420 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1421 <Self as UnusedDelimLint>::check_item(self, cx, item)
1422 }
1423
1424 fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Item) {
1425 self.in_no_bounds_pos.clear();
1426 }
1427
1428 fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) {
1429 use rustc_ast::{WhereBoundPredicate, WherePredicateKind};
1430 if let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
1431 bounded_ty,
1432 bound_generic_params,
1433 ..
1434 }) = &pred.kind
1435 && let ast::TyKind::Paren(_) = &bounded_ty.kind
1436 && bound_generic_params.is_empty()
1437 {
1438 self.with_self_ty_parens = true;
1439 }
1440 }
1441
1442 fn exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate) {
1443 assert!(!self.with_self_ty_parens);
1444 }
1445}
1446
1447declare_lint! {
1448 pub(super) UNUSED_BRACES,
1466 Warn,
1467 "unnecessary braces around an expression"
1468}
1469
1470declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]);
1471
1472impl UnusedDelimLint for UnusedBraces {
1473 const DELIM_STR: &'static str = "braces";
1474
1475 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = false;
1476
1477 fn lint(&self) -> &'static Lint {
1478 UNUSED_BRACES
1479 }
1480
1481 fn check_unused_delims_expr(
1482 &self,
1483 cx: &EarlyContext<'_>,
1484 value: &ast::Expr,
1485 ctx: UnusedDelimsCtx,
1486 followed_by_block: bool,
1487 left_pos: Option<BytePos>,
1488 right_pos: Option<BytePos>,
1489 is_kw: bool,
1490 ) {
1491 match value.kind {
1492 ast::ExprKind::Block(ref inner, None)
1493 if inner.rules == ast::BlockCheckMode::Default =>
1494 {
1495 if let [stmt] = inner.stmts.as_slice()
1520 && let ast::StmtKind::Expr(ref expr) = stmt.kind
1521 && !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
1522 && (ctx != UnusedDelimsCtx::AnonConst
1523 || (matches!(expr.kind, ast::ExprKind::Lit(_))
1524 && !expr.span.from_expansion()))
1525 && ctx != UnusedDelimsCtx::ClosureBody
1526 && !cx.sess().source_map().is_multiline(value.span)
1527 && value.attrs.is_empty()
1528 && !value.span.from_expansion()
1529 && !inner.span.from_expansion()
1530 {
1531 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1532 }
1533 }
1534 ast::ExprKind::Let(_, ref expr, _, _) => {
1535 self.check_unused_delims_expr(
1536 cx,
1537 expr,
1538 UnusedDelimsCtx::LetScrutineeExpr,
1539 followed_by_block,
1540 None,
1541 None,
1542 false,
1543 );
1544 }
1545 _ => {}
1546 }
1547 }
1548}
1549
1550impl EarlyLintPass for UnusedBraces {
1551 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1552 <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1553 }
1554
1555 #[inline]
1556 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1557 <Self as UnusedDelimLint>::check_expr(self, cx, e);
1558
1559 if let ExprKind::Repeat(_, ref anon_const) = e.kind {
1560 self.check_unused_delims_expr(
1561 cx,
1562 &anon_const.value,
1563 UnusedDelimsCtx::AnonConst,
1564 false,
1565 None,
1566 None,
1567 false,
1568 );
1569 }
1570 }
1571
1572 fn check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg) {
1573 if let ast::GenericArg::Const(ct) = arg {
1574 self.check_unused_delims_expr(
1575 cx,
1576 &ct.value,
1577 UnusedDelimsCtx::AnonConst,
1578 false,
1579 None,
1580 None,
1581 false,
1582 );
1583 }
1584 }
1585
1586 fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &ast::Variant) {
1587 if let Some(anon_const) = &v.disr_expr {
1588 self.check_unused_delims_expr(
1589 cx,
1590 &anon_const.value,
1591 UnusedDelimsCtx::AnonConst,
1592 false,
1593 None,
1594 None,
1595 false,
1596 );
1597 }
1598 }
1599
1600 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1601 match ty.kind {
1602 ast::TyKind::Array(_, ref len) => {
1603 self.check_unused_delims_expr(
1604 cx,
1605 &len.value,
1606 UnusedDelimsCtx::ArrayLenExpr,
1607 false,
1608 None,
1609 None,
1610 false,
1611 );
1612 }
1613
1614 ast::TyKind::Typeof(ref anon_const) => {
1615 self.check_unused_delims_expr(
1616 cx,
1617 &anon_const.value,
1618 UnusedDelimsCtx::AnonConst,
1619 false,
1620 None,
1621 None,
1622 false,
1623 );
1624 }
1625
1626 _ => {}
1627 }
1628 }
1629
1630 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1631 <Self as UnusedDelimLint>::check_item(self, cx, item)
1632 }
1633}
1634
1635declare_lint! {
1636 UNUSED_IMPORT_BRACES,
1661 Allow,
1662 "unnecessary braces around an imported item"
1663}
1664
1665declare_lint_pass!(UnusedImportBraces => [UNUSED_IMPORT_BRACES]);
1666
1667impl UnusedImportBraces {
1668 fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) {
1669 if let ast::UseTreeKind::Nested { ref items, .. } = use_tree.kind {
1670 for (tree, _) in items {
1672 self.check_use_tree(cx, tree, item);
1673 }
1674
1675 let [(tree, _)] = items.as_slice() else { return };
1677
1678 let node_name = match tree.kind {
1680 ast::UseTreeKind::Simple(rename) => {
1681 let orig_ident = tree.prefix.segments.last().unwrap().ident;
1682 if orig_ident.name == kw::SelfLower {
1683 return;
1684 }
1685 rename.unwrap_or(orig_ident).name
1686 }
1687 ast::UseTreeKind::Glob => sym::asterisk,
1688 ast::UseTreeKind::Nested { .. } => return,
1689 };
1690
1691 cx.emit_span_lint(
1692 UNUSED_IMPORT_BRACES,
1693 item.span,
1694 UnusedImportBracesDiag { node: node_name },
1695 );
1696 }
1697 }
1698}
1699
1700impl EarlyLintPass for UnusedImportBraces {
1701 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1702 if let ast::ItemKind::Use(ref use_tree) = item.kind {
1703 self.check_use_tree(cx, use_tree, item);
1704 }
1705 }
1706}
1707
1708declare_lint! {
1709 pub(super) UNUSED_ALLOCATION,
1728 Warn,
1729 "detects unnecessary allocations that can be eliminated"
1730}
1731
1732declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]);
1733
1734impl<'tcx> LateLintPass<'tcx> for UnusedAllocation {
1735 fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
1736 match e.kind {
1737 hir::ExprKind::Call(path_expr, [_])
1738 if let hir::ExprKind::Path(qpath) = &path_expr.kind
1739 && let Some(did) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()
1740 && cx.tcx.is_diagnostic_item(sym::box_new, did) => {}
1741 _ => return,
1742 }
1743
1744 for adj in cx.typeck_results().expr_adjustments(e) {
1745 if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(m)) = adj.kind {
1746 match m {
1747 adjustment::AutoBorrowMutability::Not => {
1748 cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag);
1749 }
1750 adjustment::AutoBorrowMutability::Mut { .. } => {
1751 cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag);
1752 }
1753 };
1754 }
1755 }
1756 }
1757}