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 if let Ok(snip) = sm.span_to_prev_source(value_span)
847 && snip.ends_with(|c: char| c.is_alphanumeric())
848 {
849 " "
850 } else {
851 ""
852 };
853
854 let hi_replace = if keep_space.1
855 && let Ok(snip) = sm.span_to_next_source(hi)
856 && !snip.starts_with(' ')
857 {
858 " "
859 } else if let Ok(snip) = sm.span_to_prev_source(value_span)
860 && snip.starts_with(|c: char| c.is_alphanumeric())
861 {
862 " "
863 } else {
864 ""
865 };
866 UnusedDelimSuggestion {
867 start_span: lo,
868 start_replace: lo_replace,
869 end_span: hi,
870 end_replace: hi_replace,
871 }
872 });
873 cx.emit_span_lint(
874 self.lint(),
875 primary_span,
876 UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion },
877 );
878 }
879
880 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
881 use rustc_ast::ExprKind::*;
882 let (value, ctx, followed_by_block, left_pos, right_pos, is_kw) = match e.kind {
883 If(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(2);
888 let right = block.span.lo();
889 (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right), true)
890 }
891
892 While(ref cond, ref block, ..)
894 if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
895 {
896 let left = e.span.lo() + rustc_span::BytePos(5);
897 let right = block.span.lo();
898 (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right), true)
899 }
900
901 ForLoop { ref iter, ref body, .. } => {
902 (iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true)
903 }
904
905 Match(ref head, _, ast::MatchKind::Prefix)
906 if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
907 {
908 let left = e.span.lo() + rustc_span::BytePos(5);
909 (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true)
910 }
911
912 Ret(Some(ref value)) => {
913 let left = e.span.lo() + rustc_span::BytePos(3);
914 (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true)
915 }
916
917 Break(label, Some(ref value)) => {
918 if label.is_some()
922 && matches!(value.kind, ast::ExprKind::Paren(ref inner)
923 if matches!(inner.kind, ast::ExprKind::Block(..)))
924 {
925 return;
926 }
927 (value, UnusedDelimsCtx::BreakValue, false, None, None, true)
928 }
929
930 Index(_, ref value, _) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false),
931
932 Assign(_, ref value, _) | AssignOp(.., ref value) => {
933 (value, UnusedDelimsCtx::AssignedValue, false, None, None, false)
934 }
935 ref call_or_other => {
937 let (args_to_check, ctx) = match *call_or_other {
938 Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg),
939 MethodCall(ref call) => (&call.args[..], UnusedDelimsCtx::MethodArg),
940 Closure(ref closure)
941 if matches!(closure.fn_decl.output, FnRetTy::Default(_)) =>
942 {
943 (&[closure.body.clone()][..], UnusedDelimsCtx::ClosureBody)
944 }
945 _ => {
947 return;
948 }
949 };
950 if e.span.ctxt().outer_expn_data().call_site.from_expansion() {
955 return;
956 }
957 for arg in args_to_check {
958 self.check_unused_delims_expr(cx, arg, ctx, false, None, None, false);
959 }
960 return;
961 }
962 };
963 self.check_unused_delims_expr(
964 cx,
965 value,
966 ctx,
967 followed_by_block,
968 left_pos,
969 right_pos,
970 is_kw,
971 );
972 }
973
974 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
975 match s.kind {
976 StmtKind::Let(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
977 if let Some((init, els)) = local.kind.init_else_opt() {
978 if els.is_some()
979 && let ExprKind::Paren(paren) = &init.kind
980 && !init.span.eq_ctxt(paren.span)
981 {
982 return;
993 }
994 let ctx = match els {
995 None => UnusedDelimsCtx::AssignedValue,
996 Some(_) => UnusedDelimsCtx::AssignedValueLetElse,
997 };
998 self.check_unused_delims_expr(cx, init, ctx, false, None, None, false);
999 }
1000 }
1001 StmtKind::Expr(ref expr) => {
1002 self.check_unused_delims_expr(
1003 cx,
1004 expr,
1005 UnusedDelimsCtx::BlockRetValue,
1006 false,
1007 None,
1008 None,
1009 false,
1010 );
1011 }
1012 _ => {}
1013 }
1014 }
1015
1016 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1017 use ast::ItemKind::*;
1018
1019 if let Const(box ast::ConstItem { expr: Some(expr), .. })
1020 | Static(box ast::StaticItem { expr: Some(expr), .. }) = &item.kind
1021 {
1022 self.check_unused_delims_expr(
1023 cx,
1024 expr,
1025 UnusedDelimsCtx::AssignedValue,
1026 false,
1027 None,
1028 None,
1029 false,
1030 );
1031 }
1032 }
1033}
1034
1035declare_lint! {
1036 pub(super) UNUSED_PARENS,
1052 Warn,
1053 "`if`, `match`, `while` and `return` do not need parentheses"
1054}
1055
1056#[derive(Default)]
1057pub(crate) struct UnusedParens {
1058 with_self_ty_parens: bool,
1059 parens_in_cast_in_lt: Vec<ast::NodeId>,
1062 in_no_bounds_pos: FxHashMap<ast::NodeId, NoBoundsException>,
1065}
1066
1067enum NoBoundsException {
1082 None,
1084 OneBound,
1087}
1088
1089impl_lint_pass!(UnusedParens => [UNUSED_PARENS]);
1090
1091impl UnusedDelimLint for UnusedParens {
1092 const DELIM_STR: &'static str = "parentheses";
1093
1094 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = true;
1095
1096 fn lint(&self) -> &'static Lint {
1097 UNUSED_PARENS
1098 }
1099
1100 fn check_unused_delims_expr(
1101 &self,
1102 cx: &EarlyContext<'_>,
1103 value: &ast::Expr,
1104 ctx: UnusedDelimsCtx,
1105 followed_by_block: bool,
1106 left_pos: Option<BytePos>,
1107 right_pos: Option<BytePos>,
1108 is_kw: bool,
1109 ) {
1110 match value.kind {
1111 ast::ExprKind::Paren(ref inner) => {
1112 if !Self::is_expr_delims_necessary(inner, ctx, followed_by_block)
1113 && value.attrs.is_empty()
1114 && !value.span.from_expansion()
1115 && (ctx != UnusedDelimsCtx::LetScrutineeExpr
1116 || !matches!(inner.kind, ast::ExprKind::Binary(
1117 rustc_span::source_map::Spanned { node, .. },
1118 _,
1119 _,
1120 ) if node.is_lazy()))
1121 && !((ctx == UnusedDelimsCtx::ReturnValue
1122 || ctx == UnusedDelimsCtx::BreakValue)
1123 && matches!(inner.kind, ast::ExprKind::Assign(_, _, _)))
1124 {
1125 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1126 }
1127 }
1128 ast::ExprKind::Let(_, ref expr, _, _) => {
1129 self.check_unused_delims_expr(
1130 cx,
1131 expr,
1132 UnusedDelimsCtx::LetScrutineeExpr,
1133 followed_by_block,
1134 None,
1135 None,
1136 false,
1137 );
1138 }
1139 _ => {}
1140 }
1141 }
1142}
1143
1144impl UnusedParens {
1145 fn check_unused_parens_pat(
1146 &self,
1147 cx: &EarlyContext<'_>,
1148 value: &ast::Pat,
1149 avoid_or: bool,
1150 avoid_mut: bool,
1151 keep_space: (bool, bool),
1152 ) {
1153 use ast::{BindingMode, PatKind};
1154
1155 if let PatKind::Paren(inner) = &value.kind {
1156 match inner.kind {
1157 PatKind::Range(..) => return,
1162 PatKind::Or(..) if avoid_or => return,
1164 PatKind::Ident(BindingMode::MUT, ..) if avoid_mut => {
1166 return;
1167 }
1168 _ => {}
1170 }
1171 let spans = if !value.span.from_expansion() {
1172 inner
1173 .span
1174 .find_ancestor_inside(value.span)
1175 .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi())))
1176 } else {
1177 None
1178 };
1179 self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false);
1180 }
1181 }
1182
1183 fn cast_followed_by_lt(&self, expr: &ast::Expr) -> Option<ast::NodeId> {
1184 if let ExprKind::Binary(op, lhs, _rhs) = &expr.kind
1185 && (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl)
1186 {
1187 let mut cur = lhs;
1188 while let ExprKind::Binary(_, _, rhs) = &cur.kind {
1189 cur = rhs;
1190 }
1191
1192 if let ExprKind::Cast(_, ty) = &cur.kind
1193 && let ast::TyKind::Paren(_) = &ty.kind
1194 {
1195 return Some(ty.id);
1196 }
1197 }
1198 None
1199 }
1200}
1201
1202impl EarlyLintPass for UnusedParens {
1203 #[inline]
1204 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1205 if let Some(ty_id) = self.cast_followed_by_lt(e) {
1206 self.parens_in_cast_in_lt.push(ty_id);
1207 }
1208
1209 match e.kind {
1210 ExprKind::Let(ref pat, _, _, _) | ExprKind::ForLoop { ref pat, .. } => {
1211 self.check_unused_parens_pat(cx, pat, false, false, (true, true));
1212 }
1213 ExprKind::If(ref cond, ref block, ref else_)
1217 if matches!(cond.peel_parens().kind, ExprKind::Let(..)) =>
1218 {
1219 self.check_unused_delims_expr(
1220 cx,
1221 cond.peel_parens(),
1222 UnusedDelimsCtx::LetScrutineeExpr,
1223 true,
1224 None,
1225 None,
1226 true,
1227 );
1228 for stmt in &block.stmts {
1229 <Self as UnusedDelimLint>::check_stmt(self, cx, stmt);
1230 }
1231 if let Some(e) = else_ {
1232 <Self as UnusedDelimLint>::check_expr(self, cx, e);
1233 }
1234 return;
1235 }
1236 ExprKind::Match(ref _expr, ref arm, _) => {
1237 for a in arm {
1238 if let Some(body) = &a.body {
1239 self.check_unused_delims_expr(
1240 cx,
1241 body,
1242 UnusedDelimsCtx::MatchArmExpr,
1243 false,
1244 None,
1245 None,
1246 true,
1247 );
1248 }
1249 }
1250 }
1251 _ => {}
1252 }
1253
1254 <Self as UnusedDelimLint>::check_expr(self, cx, e)
1255 }
1256
1257 fn check_expr_post(&mut self, _cx: &EarlyContext<'_>, e: &ast::Expr) {
1258 if let Some(ty_id) = self.cast_followed_by_lt(e) {
1259 let id = self
1260 .parens_in_cast_in_lt
1261 .pop()
1262 .expect("check_expr and check_expr_post must balance");
1263 assert_eq!(
1264 id, ty_id,
1265 "check_expr, check_ty, and check_expr_post are called, in that order, by the visitor"
1266 );
1267 }
1268 }
1269
1270 fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
1271 use ast::Mutability;
1272 use ast::PatKind::*;
1273 let keep_space = (false, false);
1274 match &p.kind {
1275 Paren(_)
1277 | Missing | Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None)
1279 | Path(..) | Err(_) => {},
1280 TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
1282 self.check_unused_parens_pat(cx, p, false, false, keep_space);
1283 },
1284 Struct(_, _, fps, _) => for f in fps {
1285 self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space);
1286 },
1287 Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
1289 Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
1292 }
1293 }
1294
1295 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1296 if let StmtKind::Let(ref local) = s.kind {
1297 self.check_unused_parens_pat(cx, &local.pat, true, false, (true, false));
1298 }
1299
1300 <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1301 }
1302
1303 fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) {
1304 self.check_unused_parens_pat(cx, ¶m.pat, true, false, (false, false));
1305 }
1306
1307 fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
1308 self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false));
1309 }
1310
1311 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1312 if let ast::TyKind::Paren(_) = ty.kind
1313 && Some(&ty.id) == self.parens_in_cast_in_lt.last()
1314 {
1315 return;
1316 }
1317 match &ty.kind {
1318 ast::TyKind::Array(_, len) => {
1319 self.check_unused_delims_expr(
1320 cx,
1321 &len.value,
1322 UnusedDelimsCtx::ArrayLenExpr,
1323 false,
1324 None,
1325 None,
1326 false,
1327 );
1328 }
1329 ast::TyKind::Paren(r) => {
1330 let unused_parens = match &r.kind {
1331 ast::TyKind::ImplTrait(_, bounds) | ast::TyKind::TraitObject(bounds, _) => {
1332 match self.in_no_bounds_pos.get(&ty.id) {
1333 Some(NoBoundsException::None) => false,
1334 Some(NoBoundsException::OneBound) => bounds.len() <= 1,
1335 None => true,
1336 }
1337 }
1338 ast::TyKind::FnPtr(b) => {
1339 !self.with_self_ty_parens || b.generic_params.is_empty()
1340 }
1341 _ => true,
1342 };
1343
1344 if unused_parens {
1345 let spans = (!ty.span.from_expansion())
1346 .then(|| {
1347 r.span
1348 .find_ancestor_inside(ty.span)
1349 .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
1350 })
1351 .flatten();
1352
1353 self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false);
1354 }
1355
1356 self.with_self_ty_parens = false;
1357 }
1358 ast::TyKind::Ref(_, mut_ty) | ast::TyKind::Ptr(mut_ty) => {
1359 let own_constraint = self.in_no_bounds_pos.get(&ty.id);
1362 let constraint = match own_constraint {
1363 Some(NoBoundsException::None) => NoBoundsException::None,
1364 Some(NoBoundsException::OneBound) => NoBoundsException::OneBound,
1365 None => NoBoundsException::OneBound,
1366 };
1367 self.in_no_bounds_pos.insert(mut_ty.ty.id, constraint);
1368 }
1369 ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
1370 for i in 0..bounds.len() {
1371 let is_last = i == bounds.len() - 1;
1372
1373 if let ast::GenericBound::Trait(poly_trait_ref) = &bounds[i] {
1374 let fn_with_explicit_ret_ty = if let [.., segment] =
1375 &*poly_trait_ref.trait_ref.path.segments
1376 && let Some(args) = segment.args.as_ref()
1377 && let ast::GenericArgs::Parenthesized(paren_args) = &**args
1378 && let ast::FnRetTy::Ty(ret_ty) = &paren_args.output
1379 {
1380 self.in_no_bounds_pos.insert(
1381 ret_ty.id,
1382 if is_last {
1383 NoBoundsException::OneBound
1384 } else {
1385 NoBoundsException::None
1386 },
1387 );
1388
1389 true
1390 } else {
1391 false
1392 };
1393
1394 let dyn2015_exception = cx.sess().psess.edition == Edition2015
1399 && matches!(ty.kind, ast::TyKind::TraitObject(..))
1400 && i == 0
1401 && poly_trait_ref
1402 .trait_ref
1403 .path
1404 .segments
1405 .first()
1406 .map(|s| s.ident.name == kw::PathRoot)
1407 .unwrap_or(false);
1408
1409 if let ast::Parens::Yes = poly_trait_ref.parens
1410 && (is_last || !fn_with_explicit_ret_ty)
1411 && !dyn2015_exception
1412 {
1413 let s = poly_trait_ref.span;
1414 let spans = (!s.from_expansion()).then(|| {
1415 (
1416 s.with_hi(s.lo() + rustc_span::BytePos(1)),
1417 s.with_lo(s.hi() - rustc_span::BytePos(1)),
1418 )
1419 });
1420
1421 self.emit_unused_delims(
1422 cx,
1423 poly_trait_ref.span,
1424 spans,
1425 "type",
1426 (false, false),
1427 false,
1428 );
1429 }
1430 }
1431 }
1432 }
1433 _ => {}
1434 }
1435 }
1436
1437 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1438 <Self as UnusedDelimLint>::check_item(self, cx, item)
1439 }
1440
1441 fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Item) {
1442 self.in_no_bounds_pos.clear();
1443 }
1444
1445 fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) {
1446 use rustc_ast::{WhereBoundPredicate, WherePredicateKind};
1447 if let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
1448 bounded_ty,
1449 bound_generic_params,
1450 ..
1451 }) = &pred.kind
1452 && let ast::TyKind::Paren(_) = &bounded_ty.kind
1453 && bound_generic_params.is_empty()
1454 {
1455 self.with_self_ty_parens = true;
1456 }
1457 }
1458
1459 fn exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate) {
1460 assert!(!self.with_self_ty_parens);
1461 }
1462}
1463
1464declare_lint! {
1465 pub(super) UNUSED_BRACES,
1483 Warn,
1484 "unnecessary braces around an expression"
1485}
1486
1487declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]);
1488
1489impl UnusedDelimLint for UnusedBraces {
1490 const DELIM_STR: &'static str = "braces";
1491
1492 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = false;
1493
1494 fn lint(&self) -> &'static Lint {
1495 UNUSED_BRACES
1496 }
1497
1498 fn check_unused_delims_expr(
1499 &self,
1500 cx: &EarlyContext<'_>,
1501 value: &ast::Expr,
1502 ctx: UnusedDelimsCtx,
1503 followed_by_block: bool,
1504 left_pos: Option<BytePos>,
1505 right_pos: Option<BytePos>,
1506 is_kw: bool,
1507 ) {
1508 match value.kind {
1509 ast::ExprKind::Block(ref inner, None)
1510 if inner.rules == ast::BlockCheckMode::Default =>
1511 {
1512 if let [stmt] = inner.stmts.as_slice()
1537 && let ast::StmtKind::Expr(ref expr) = stmt.kind
1538 && !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
1539 && (ctx != UnusedDelimsCtx::AnonConst
1540 || (matches!(expr.kind, ast::ExprKind::Lit(_))
1541 && !expr.span.from_expansion()))
1542 && ctx != UnusedDelimsCtx::ClosureBody
1543 && !cx.sess().source_map().is_multiline(value.span)
1544 && value.attrs.is_empty()
1545 && !value.span.from_expansion()
1546 && !inner.span.from_expansion()
1547 {
1548 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1549 }
1550 }
1551 ast::ExprKind::Let(_, ref expr, _, _) => {
1552 self.check_unused_delims_expr(
1553 cx,
1554 expr,
1555 UnusedDelimsCtx::LetScrutineeExpr,
1556 followed_by_block,
1557 None,
1558 None,
1559 false,
1560 );
1561 }
1562 _ => {}
1563 }
1564 }
1565}
1566
1567impl EarlyLintPass for UnusedBraces {
1568 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1569 <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1570 }
1571
1572 #[inline]
1573 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1574 <Self as UnusedDelimLint>::check_expr(self, cx, e);
1575
1576 if let ExprKind::Repeat(_, ref anon_const) = e.kind {
1577 self.check_unused_delims_expr(
1578 cx,
1579 &anon_const.value,
1580 UnusedDelimsCtx::AnonConst,
1581 false,
1582 None,
1583 None,
1584 false,
1585 );
1586 }
1587 }
1588
1589 fn check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg) {
1590 if let ast::GenericArg::Const(ct) = arg {
1591 self.check_unused_delims_expr(
1592 cx,
1593 &ct.value,
1594 UnusedDelimsCtx::AnonConst,
1595 false,
1596 None,
1597 None,
1598 false,
1599 );
1600 }
1601 }
1602
1603 fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &ast::Variant) {
1604 if let Some(anon_const) = &v.disr_expr {
1605 self.check_unused_delims_expr(
1606 cx,
1607 &anon_const.value,
1608 UnusedDelimsCtx::AnonConst,
1609 false,
1610 None,
1611 None,
1612 false,
1613 );
1614 }
1615 }
1616
1617 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1618 match ty.kind {
1619 ast::TyKind::Array(_, ref len) => {
1620 self.check_unused_delims_expr(
1621 cx,
1622 &len.value,
1623 UnusedDelimsCtx::ArrayLenExpr,
1624 false,
1625 None,
1626 None,
1627 false,
1628 );
1629 }
1630
1631 ast::TyKind::Typeof(ref anon_const) => {
1632 self.check_unused_delims_expr(
1633 cx,
1634 &anon_const.value,
1635 UnusedDelimsCtx::AnonConst,
1636 false,
1637 None,
1638 None,
1639 false,
1640 );
1641 }
1642
1643 _ => {}
1644 }
1645 }
1646
1647 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1648 <Self as UnusedDelimLint>::check_item(self, cx, item)
1649 }
1650}
1651
1652declare_lint! {
1653 UNUSED_IMPORT_BRACES,
1678 Allow,
1679 "unnecessary braces around an imported item"
1680}
1681
1682declare_lint_pass!(UnusedImportBraces => [UNUSED_IMPORT_BRACES]);
1683
1684impl UnusedImportBraces {
1685 fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) {
1686 if let ast::UseTreeKind::Nested { ref items, .. } = use_tree.kind {
1687 for (tree, _) in items {
1689 self.check_use_tree(cx, tree, item);
1690 }
1691
1692 let [(tree, _)] = items.as_slice() else { return };
1694
1695 let node_name = match tree.kind {
1697 ast::UseTreeKind::Simple(rename) => {
1698 let orig_ident = tree.prefix.segments.last().unwrap().ident;
1699 if orig_ident.name == kw::SelfLower {
1700 return;
1701 }
1702 rename.unwrap_or(orig_ident).name
1703 }
1704 ast::UseTreeKind::Glob => sym::asterisk,
1705 ast::UseTreeKind::Nested { .. } => return,
1706 };
1707
1708 cx.emit_span_lint(
1709 UNUSED_IMPORT_BRACES,
1710 item.span,
1711 UnusedImportBracesDiag { node: node_name },
1712 );
1713 }
1714 }
1715}
1716
1717impl EarlyLintPass for UnusedImportBraces {
1718 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1719 if let ast::ItemKind::Use(ref use_tree) = item.kind {
1720 self.check_use_tree(cx, use_tree, item);
1721 }
1722 }
1723}
1724
1725declare_lint! {
1726 pub(super) UNUSED_ALLOCATION,
1745 Warn,
1746 "detects unnecessary allocations that can be eliminated"
1747}
1748
1749declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]);
1750
1751impl<'tcx> LateLintPass<'tcx> for UnusedAllocation {
1752 fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
1753 match e.kind {
1754 hir::ExprKind::Call(path_expr, [_])
1755 if let hir::ExprKind::Path(qpath) = &path_expr.kind
1756 && let Some(did) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()
1757 && cx.tcx.is_diagnostic_item(sym::box_new, did) => {}
1758 _ => return,
1759 }
1760
1761 for adj in cx.typeck_results().expr_adjustments(e) {
1762 if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(m)) = adj.kind {
1763 match m {
1764 adjustment::AutoBorrowMutability::Not => {
1765 cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag);
1766 }
1767 adjustment::AutoBorrowMutability::Mut { .. } => {
1768 cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag);
1769 }
1770 };
1771 }
1772 }
1773 }
1774}