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 return Some(MustUsePath::Suppressed);
278 }
279 let parent_mod_did = cx.tcx.parent_module(expr.hir_id).to_def_id();
280 let is_uninhabited =
281 |t: Ty<'tcx>| !t.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env());
282 if is_uninhabited(ty) {
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, args)
298 if cx.tcx.is_diagnostic_item(sym::Result, def.did())
299 && args.type_at(0).is_unit()
300 && is_uninhabited(args.type_at(1)) =>
301 {
302 Some(MustUsePath::Suppressed)
303 }
304 ty::Adt(def, args)
306 if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
307 && args.type_at(1).is_unit()
308 && is_uninhabited(args.type_at(0)) =>
309 {
310 Some(MustUsePath::Suppressed)
311 }
312 ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
313 ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => {
314 elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied())
315 .filter_only_self()
317 .find_map(|(pred, _span)| {
318 if let ty::ClauseKind::Trait(ref poly_trait_predicate) =
320 pred.kind().skip_binder()
321 {
322 let def_id = poly_trait_predicate.trait_ref.def_id;
323
324 is_def_must_use(cx, def_id, span)
325 } else {
326 None
327 }
328 })
329 .map(|inner| MustUsePath::Opaque(Box::new(inner)))
330 }
331 ty::Dynamic(binders, _) => binders.iter().find_map(|predicate| {
332 if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
333 {
334 let def_id = trait_ref.def_id;
335 is_def_must_use(cx, def_id, span)
336 .map(|inner| MustUsePath::TraitObject(Box::new(inner)))
337 } else {
338 None
339 }
340 }),
341 ty::Tuple(tys) => {
342 let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind {
343 debug_assert_eq!(elem_exprs.len(), tys.len());
344 elem_exprs
345 } else {
346 &[]
347 };
348
349 let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr));
351
352 let nested_must_use = tys
353 .iter()
354 .zip(elem_exprs)
355 .enumerate()
356 .filter_map(|(i, (ty, expr))| {
357 is_ty_must_use(cx, ty, expr, expr.span).map(|path| (i, path))
358 })
359 .collect::<Vec<_>>();
360
361 if !nested_must_use.is_empty() {
362 Some(MustUsePath::TupleElement(nested_must_use))
363 } else {
364 None
365 }
366 }
367 ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) {
368 Some(0) | None => None,
370 Some(len) => is_ty_must_use(cx, ty, expr, span)
372 .map(|inner| MustUsePath::Array(Box::new(inner), len)),
373 },
374 ty::Closure(..) | ty::CoroutineClosure(..) => Some(MustUsePath::Closure(span)),
375 ty::Coroutine(def_id, ..) => {
376 let must_use = if cx.tcx.coroutine_is_async(def_id) {
378 let def_id = cx.tcx.lang_items().future_trait()?;
379 is_def_must_use(cx, def_id, span)
380 .map(|inner| MustUsePath::Opaque(Box::new(inner)))
381 } else {
382 None
383 };
384 must_use.or(Some(MustUsePath::Coroutine(span)))
385 }
386 _ => None,
387 }
388 }
389
390 fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option<MustUsePath> {
391 if let Some(reason) = find_attr!(
392 cx.tcx.get_all_attrs(def_id),
393 AttributeKind::MustUse { reason, .. } => reason
394 ) {
395 Some(MustUsePath::Def(span, def_id, *reason))
397 } else {
398 None
399 }
400 }
401
402 fn check_must_use_def(
405 cx: &LateContext<'_>,
406 def_id: DefId,
407 span: Span,
408 descr_pre_path: &str,
409 descr_post_path: &str,
410 expr_is_from_block: bool,
411 ) -> bool {
412 is_def_must_use(cx, def_id, span)
413 .map(|must_use_path| {
414 emit_must_use_untranslated(
415 cx,
416 &must_use_path,
417 descr_pre_path,
418 descr_post_path,
419 1,
420 false,
421 expr_is_from_block,
422 )
423 })
424 .is_some()
425 }
426
427 #[instrument(skip(cx), level = "debug")]
428 fn emit_must_use_untranslated(
429 cx: &LateContext<'_>,
430 path: &MustUsePath,
431 descr_pre: &str,
432 descr_post: &str,
433 plural_len: usize,
434 is_inner: bool,
435 expr_is_from_block: bool,
436 ) {
437 let plural_suffix = pluralize!(plural_len);
438
439 match path {
440 MustUsePath::Suppressed => {}
441 MustUsePath::Boxed(path) => {
442 let descr_pre = &format!("{descr_pre}boxed ");
443 emit_must_use_untranslated(
444 cx,
445 path,
446 descr_pre,
447 descr_post,
448 plural_len,
449 true,
450 expr_is_from_block,
451 );
452 }
453 MustUsePath::Pinned(path) => {
454 let descr_pre = &format!("{descr_pre}pinned ");
455 emit_must_use_untranslated(
456 cx,
457 path,
458 descr_pre,
459 descr_post,
460 plural_len,
461 true,
462 expr_is_from_block,
463 );
464 }
465 MustUsePath::Opaque(path) => {
466 let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of ");
467 emit_must_use_untranslated(
468 cx,
469 path,
470 descr_pre,
471 descr_post,
472 plural_len,
473 true,
474 expr_is_from_block,
475 );
476 }
477 MustUsePath::TraitObject(path) => {
478 let descr_post = &format!(" trait object{plural_suffix}{descr_post}");
479 emit_must_use_untranslated(
480 cx,
481 path,
482 descr_pre,
483 descr_post,
484 plural_len,
485 true,
486 expr_is_from_block,
487 );
488 }
489 MustUsePath::TupleElement(elems) => {
490 for (index, path) in elems {
491 let descr_post = &format!(" in tuple element {index}");
492 emit_must_use_untranslated(
493 cx,
494 path,
495 descr_pre,
496 descr_post,
497 plural_len,
498 true,
499 expr_is_from_block,
500 );
501 }
502 }
503 MustUsePath::Array(path, len) => {
504 let descr_pre = &format!("{descr_pre}array{plural_suffix} of ");
505 emit_must_use_untranslated(
506 cx,
507 path,
508 descr_pre,
509 descr_post,
510 plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)),
511 true,
512 expr_is_from_block,
513 );
514 }
515 MustUsePath::Closure(span) => {
516 cx.emit_span_lint(
517 UNUSED_MUST_USE,
518 *span,
519 UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post },
520 );
521 }
522 MustUsePath::Coroutine(span) => {
523 cx.emit_span_lint(
524 UNUSED_MUST_USE,
525 *span,
526 UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post },
527 );
528 }
529 MustUsePath::Def(span, def_id, reason) => {
530 let span = span.find_ancestor_not_from_macro().unwrap_or(*span);
531 cx.emit_span_lint(
532 UNUSED_MUST_USE,
533 span,
534 UnusedDef {
535 pre: descr_pre,
536 post: descr_post,
537 cx,
538 def_id: *def_id,
539 note: *reason,
540 suggestion: (!is_inner).then_some(if expr_is_from_block {
541 UnusedDefSuggestion::BlockTailExpr {
542 before_span: span.shrink_to_lo(),
543 after_span: span.shrink_to_hi(),
544 }
545 } else {
546 UnusedDefSuggestion::NormalExpr { span: span.shrink_to_lo() }
547 }),
548 },
549 );
550 }
551 }
552 }
553 }
554}
555
556declare_lint! {
557 pub PATH_STATEMENTS,
573 Warn,
574 "path statements with no effect"
575}
576
577declare_lint_pass!(PathStatements => [PATH_STATEMENTS]);
578
579impl<'tcx> LateLintPass<'tcx> for PathStatements {
580 fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
581 if let hir::StmtKind::Semi(expr) = s.kind
582 && let hir::ExprKind::Path(_) = expr.kind
583 {
584 let ty = cx.typeck_results().expr_ty(expr);
585 if ty.needs_drop(cx.tcx, cx.typing_env()) {
586 let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) {
587 PathStatementDropSub::Suggestion { span: s.span, snippet }
588 } else {
589 PathStatementDropSub::Help { span: s.span }
590 };
591 cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub })
592 } else {
593 cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect);
594 }
595 }
596 }
597}
598
599#[derive(Copy, Clone, Debug, PartialEq, Eq)]
600enum UnusedDelimsCtx {
601 FunctionArg,
602 MethodArg,
603 AssignedValue,
604 AssignedValueLetElse,
605 IfCond,
606 WhileCond,
607 ForIterExpr,
608 MatchScrutineeExpr,
609 ReturnValue,
610 BlockRetValue,
611 BreakValue,
612 LetScrutineeExpr,
613 ArrayLenExpr,
614 AnonConst,
615 MatchArmExpr,
616 IndexExpr,
617 ClosureBody,
618}
619
620impl From<UnusedDelimsCtx> for &'static str {
621 fn from(ctx: UnusedDelimsCtx) -> &'static str {
622 match ctx {
623 UnusedDelimsCtx::FunctionArg => "function argument",
624 UnusedDelimsCtx::MethodArg => "method argument",
625 UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => {
626 "assigned value"
627 }
628 UnusedDelimsCtx::IfCond => "`if` condition",
629 UnusedDelimsCtx::WhileCond => "`while` condition",
630 UnusedDelimsCtx::ForIterExpr => "`for` iterator expression",
631 UnusedDelimsCtx::MatchScrutineeExpr => "`match` scrutinee expression",
632 UnusedDelimsCtx::ReturnValue => "`return` value",
633 UnusedDelimsCtx::BlockRetValue => "block return value",
634 UnusedDelimsCtx::BreakValue => "`break` value",
635 UnusedDelimsCtx::LetScrutineeExpr => "`let` scrutinee expression",
636 UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression",
637 UnusedDelimsCtx::MatchArmExpr => "match arm expression",
638 UnusedDelimsCtx::IndexExpr => "index expression",
639 UnusedDelimsCtx::ClosureBody => "closure body",
640 }
641 }
642}
643
644trait UnusedDelimLint {
646 const DELIM_STR: &'static str;
647
648 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool;
660
661 fn lint(&self) -> &'static Lint;
663
664 fn check_unused_delims_expr(
665 &self,
666 cx: &EarlyContext<'_>,
667 value: &ast::Expr,
668 ctx: UnusedDelimsCtx,
669 followed_by_block: bool,
670 left_pos: Option<BytePos>,
671 right_pos: Option<BytePos>,
672 is_kw: bool,
673 );
674
675 fn is_expr_delims_necessary(
676 inner: &ast::Expr,
677 ctx: UnusedDelimsCtx,
678 followed_by_block: bool,
679 ) -> bool {
680 let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
681
682 if followed_by_else {
683 match inner.kind {
684 ast::ExprKind::Binary(op, ..) if op.node.is_lazy() => return true,
685 _ if classify::expr_trailing_brace(inner).is_some() => return true,
686 _ => {}
687 }
688 }
689
690 if let ast::ExprKind::Range(..) = inner.kind
692 && matches!(ctx, UnusedDelimsCtx::LetScrutineeExpr)
693 {
694 return true;
695 }
696
697 if matches!(inner.kind, ast::ExprKind::AddrOf(ast::BorrowKind::Raw, ..)) {
701 return true;
702 }
703
704 {
734 let mut innermost = inner;
735 loop {
736 innermost = match &innermost.kind {
737 ExprKind::Binary(_op, lhs, _rhs) => lhs,
738 ExprKind::Call(fn_, _params) => fn_,
739 ExprKind::Cast(expr, _ty) => expr,
740 ExprKind::Type(expr, _ty) => expr,
741 ExprKind::Index(base, _subscript, _) => base,
742 _ => break,
743 };
744 if !classify::expr_requires_semi_to_be_stmt(innermost) {
745 return true;
746 }
747 }
748 }
749
750 if !followed_by_block {
753 return false;
754 }
755
756 {
758 let mut innermost = inner;
759 loop {
760 innermost = match &innermost.kind {
761 ExprKind::AddrOf(_, _, expr) => expr,
762 _ => {
763 if parser::contains_exterior_struct_lit(innermost) {
764 return true;
765 } else {
766 break;
767 }
768 }
769 }
770 }
771 }
772
773 let mut innermost = inner;
774 loop {
775 innermost = match &innermost.kind {
776 ExprKind::Unary(_op, expr) => expr,
777 ExprKind::Binary(_op, _lhs, rhs) => rhs,
778 ExprKind::AssignOp(_op, _lhs, rhs) => rhs,
779 ExprKind::Assign(_lhs, rhs, _span) => rhs,
780
781 ExprKind::Ret(_) | ExprKind::Yield(..) | ExprKind::Yeet(..) => return true,
782
783 ExprKind::Break(_label, None) => return false,
784 ExprKind::Break(_label, Some(break_expr)) => {
785 return matches!(break_expr.kind, ExprKind::Block(..));
786 }
787
788 ExprKind::Range(_lhs, Some(rhs), _limits) => {
789 return matches!(rhs.kind, ExprKind::Block(..));
790 }
791
792 _ => return parser::contains_exterior_struct_lit(inner),
793 }
794 }
795 }
796
797 fn emit_unused_delims_expr(
798 &self,
799 cx: &EarlyContext<'_>,
800 value: &ast::Expr,
801 ctx: UnusedDelimsCtx,
802 left_pos: Option<BytePos>,
803 right_pos: Option<BytePos>,
804 is_kw: bool,
805 ) {
806 let span_with_attrs = match value.kind {
807 ast::ExprKind::Block(ref block, None) if let [stmt] = block.stmts.as_slice() => {
808 if let Some(attr_lo) = stmt.attrs().iter().map(|attr| attr.span.lo()).min() {
811 stmt.span.with_lo(attr_lo)
812 } else {
813 stmt.span
814 }
815 }
816 ast::ExprKind::Paren(ref expr) => {
817 if let Some(attr_lo) = expr.attrs.iter().map(|attr| attr.span.lo()).min() {
820 expr.span.with_lo(attr_lo)
821 } else {
822 expr.span
823 }
824 }
825 _ => return,
826 };
827 let spans = span_with_attrs
828 .find_ancestor_inside(value.span)
829 .map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi())));
830 let keep_space = (
831 left_pos.is_some_and(|s| s >= value.span.lo()),
832 right_pos.is_some_and(|s| s <= value.span.hi()),
833 );
834 self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space, is_kw);
835 }
836
837 fn emit_unused_delims(
838 &self,
839 cx: &EarlyContext<'_>,
840 value_span: Span,
841 spans: Option<(Span, Span)>,
842 msg: &str,
843 keep_space: (bool, bool),
844 is_kw: bool,
845 ) {
846 let primary_span = if let Some((lo, hi)) = spans {
847 if hi.is_empty() {
848 return;
850 }
851 MultiSpan::from(vec![lo, hi])
852 } else {
853 MultiSpan::from(value_span)
854 };
855 let suggestion = spans.map(|(lo, hi)| {
856 let sm = cx.sess().source_map();
857 let lo_replace = if (keep_space.0 || is_kw)
858 && let Ok(snip) = sm.span_to_prev_source(lo)
859 && !snip.ends_with(' ')
860 {
861 " "
862 } else if let Ok(snip) = sm.span_to_prev_source(value_span)
863 && snip.ends_with(|c: char| c.is_alphanumeric())
864 {
865 " "
866 } else {
867 ""
868 };
869
870 let hi_replace = if keep_space.1
871 && let Ok(snip) = sm.span_to_next_source(hi)
872 && !snip.starts_with(' ')
873 {
874 " "
875 } else if let Ok(snip) = sm.span_to_prev_source(value_span)
876 && snip.starts_with(|c: char| c.is_alphanumeric())
877 {
878 " "
879 } else {
880 ""
881 };
882 UnusedDelimSuggestion {
883 start_span: lo,
884 start_replace: lo_replace,
885 end_span: hi,
886 end_replace: hi_replace,
887 }
888 });
889 cx.emit_span_lint(
890 self.lint(),
891 primary_span,
892 UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion },
893 );
894 }
895
896 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
897 use rustc_ast::ExprKind::*;
898 let (value, ctx, followed_by_block, left_pos, right_pos, is_kw) = match e.kind {
899 If(ref cond, ref block, _)
901 if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
902 {
903 let left = e.span.lo() + rustc_span::BytePos(2);
904 let right = block.span.lo();
905 (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right), true)
906 }
907
908 While(ref cond, ref block, ..)
910 if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
911 {
912 let left = e.span.lo() + rustc_span::BytePos(5);
913 let right = block.span.lo();
914 (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right), true)
915 }
916
917 ForLoop { ref iter, ref body, .. } => {
918 (iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true)
919 }
920
921 Match(ref head, _, ast::MatchKind::Prefix)
922 if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
923 {
924 let left = e.span.lo() + rustc_span::BytePos(5);
925 (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true)
926 }
927
928 Ret(Some(ref value)) => {
929 let left = e.span.lo() + rustc_span::BytePos(3);
930 (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true)
931 }
932
933 Break(label, Some(ref value)) => {
934 if label.is_some()
938 && matches!(value.kind, ast::ExprKind::Paren(ref inner)
939 if matches!(inner.kind, ast::ExprKind::Block(..)))
940 {
941 return;
942 }
943 (value, UnusedDelimsCtx::BreakValue, false, None, None, true)
944 }
945
946 Index(_, ref value, _) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false),
947
948 Assign(_, ref value, _) | AssignOp(.., ref value) => {
949 (value, UnusedDelimsCtx::AssignedValue, false, None, None, false)
950 }
951 ref call_or_other => {
953 let (args_to_check, ctx) = match *call_or_other {
954 Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg),
955 MethodCall(ref call) => (&call.args[..], UnusedDelimsCtx::MethodArg),
956 Closure(ref closure)
957 if matches!(closure.fn_decl.output, FnRetTy::Default(_)) =>
958 {
959 (&[closure.body.clone()][..], UnusedDelimsCtx::ClosureBody)
960 }
961 _ => {
963 return;
964 }
965 };
966 if e.span.ctxt().outer_expn_data().call_site.from_expansion() {
971 return;
972 }
973 for arg in args_to_check {
974 self.check_unused_delims_expr(cx, arg, ctx, false, None, None, false);
975 }
976 return;
977 }
978 };
979 self.check_unused_delims_expr(
980 cx,
981 value,
982 ctx,
983 followed_by_block,
984 left_pos,
985 right_pos,
986 is_kw,
987 );
988 }
989
990 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
991 match s.kind {
992 StmtKind::Let(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
993 if let Some((init, els)) = local.kind.init_else_opt() {
994 if els.is_some()
995 && let ExprKind::Paren(paren) = &init.kind
996 && !init.span.eq_ctxt(paren.span)
997 {
998 return;
1009 }
1010 let ctx = match els {
1011 None => UnusedDelimsCtx::AssignedValue,
1012 Some(_) => UnusedDelimsCtx::AssignedValueLetElse,
1013 };
1014 self.check_unused_delims_expr(cx, init, ctx, false, None, None, false);
1015 }
1016 }
1017 StmtKind::Expr(ref expr) => {
1018 self.check_unused_delims_expr(
1019 cx,
1020 expr,
1021 UnusedDelimsCtx::BlockRetValue,
1022 false,
1023 None,
1024 None,
1025 false,
1026 );
1027 }
1028 _ => {}
1029 }
1030 }
1031
1032 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1033 use ast::ItemKind::*;
1034
1035 if let Const(box ast::ConstItem { expr: Some(expr), .. })
1036 | Static(box ast::StaticItem { expr: Some(expr), .. }) = &item.kind
1037 {
1038 self.check_unused_delims_expr(
1039 cx,
1040 expr,
1041 UnusedDelimsCtx::AssignedValue,
1042 false,
1043 None,
1044 None,
1045 false,
1046 );
1047 }
1048 }
1049}
1050
1051declare_lint! {
1052 pub(super) UNUSED_PARENS,
1068 Warn,
1069 "`if`, `match`, `while` and `return` do not need parentheses"
1070}
1071
1072#[derive(Default)]
1073pub(crate) struct UnusedParens {
1074 with_self_ty_parens: bool,
1075 parens_in_cast_in_lt: Vec<ast::NodeId>,
1078 in_no_bounds_pos: FxHashMap<ast::NodeId, NoBoundsException>,
1081}
1082
1083enum NoBoundsException {
1098 None,
1100 OneBound,
1103}
1104
1105impl_lint_pass!(UnusedParens => [UNUSED_PARENS]);
1106
1107impl UnusedDelimLint for UnusedParens {
1108 const DELIM_STR: &'static str = "parentheses";
1109
1110 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = true;
1111
1112 fn lint(&self) -> &'static Lint {
1113 UNUSED_PARENS
1114 }
1115
1116 fn check_unused_delims_expr(
1117 &self,
1118 cx: &EarlyContext<'_>,
1119 value: &ast::Expr,
1120 ctx: UnusedDelimsCtx,
1121 followed_by_block: bool,
1122 left_pos: Option<BytePos>,
1123 right_pos: Option<BytePos>,
1124 is_kw: bool,
1125 ) {
1126 match value.kind {
1127 ast::ExprKind::Paren(ref inner) => {
1128 if !Self::is_expr_delims_necessary(inner, ctx, followed_by_block)
1129 && value.attrs.is_empty()
1130 && !value.span.from_expansion()
1131 && (ctx != UnusedDelimsCtx::LetScrutineeExpr
1132 || !matches!(inner.kind, ast::ExprKind::Binary(
1133 rustc_span::source_map::Spanned { node, .. },
1134 _,
1135 _,
1136 ) if node.is_lazy()))
1137 && !((ctx == UnusedDelimsCtx::ReturnValue
1138 || ctx == UnusedDelimsCtx::BreakValue)
1139 && matches!(inner.kind, ast::ExprKind::Assign(_, _, _)))
1140 {
1141 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1142 }
1143 }
1144 ast::ExprKind::Let(_, ref expr, _, _) => {
1145 self.check_unused_delims_expr(
1146 cx,
1147 expr,
1148 UnusedDelimsCtx::LetScrutineeExpr,
1149 followed_by_block,
1150 None,
1151 None,
1152 false,
1153 );
1154 }
1155 _ => {}
1156 }
1157 }
1158}
1159
1160impl UnusedParens {
1161 fn check_unused_parens_pat(
1162 &self,
1163 cx: &EarlyContext<'_>,
1164 value: &ast::Pat,
1165 avoid_or: bool,
1166 avoid_mut: bool,
1167 keep_space: (bool, bool),
1168 ) {
1169 use ast::{BindingMode, PatKind};
1170
1171 if let PatKind::Paren(inner) = &value.kind {
1172 match inner.kind {
1173 PatKind::Range(..) => return,
1178 PatKind::Or(..) if avoid_or => return,
1180 PatKind::Ident(BindingMode::MUT, ..) if avoid_mut => {
1182 return;
1183 }
1184 _ => {}
1186 }
1187 let spans = if !value.span.from_expansion() {
1188 inner
1189 .span
1190 .find_ancestor_inside(value.span)
1191 .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi())))
1192 } else {
1193 None
1194 };
1195 self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false);
1196 }
1197 }
1198
1199 fn cast_followed_by_lt(&self, expr: &ast::Expr) -> Option<ast::NodeId> {
1200 if let ExprKind::Binary(op, lhs, _rhs) = &expr.kind
1201 && (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl)
1202 {
1203 let mut cur = lhs;
1204 while let ExprKind::Binary(_, _, rhs) = &cur.kind {
1205 cur = rhs;
1206 }
1207
1208 if let ExprKind::Cast(_, ty) = &cur.kind
1209 && let ast::TyKind::Paren(_) = &ty.kind
1210 {
1211 return Some(ty.id);
1212 }
1213 }
1214 None
1215 }
1216}
1217
1218impl EarlyLintPass for UnusedParens {
1219 #[inline]
1220 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1221 if let Some(ty_id) = self.cast_followed_by_lt(e) {
1222 self.parens_in_cast_in_lt.push(ty_id);
1223 }
1224
1225 match e.kind {
1226 ExprKind::Let(ref pat, _, _, _) | ExprKind::ForLoop { ref pat, .. } => {
1227 self.check_unused_parens_pat(cx, pat, false, false, (true, true));
1228 }
1229 ExprKind::If(ref cond, ref block, ref else_)
1233 if matches!(cond.peel_parens().kind, ExprKind::Let(..)) =>
1234 {
1235 self.check_unused_delims_expr(
1236 cx,
1237 cond.peel_parens(),
1238 UnusedDelimsCtx::LetScrutineeExpr,
1239 true,
1240 None,
1241 None,
1242 true,
1243 );
1244 for stmt in &block.stmts {
1245 <Self as UnusedDelimLint>::check_stmt(self, cx, stmt);
1246 }
1247 if let Some(e) = else_ {
1248 <Self as UnusedDelimLint>::check_expr(self, cx, e);
1249 }
1250 return;
1251 }
1252 ExprKind::Match(ref _expr, ref arm, _) => {
1253 for a in arm {
1254 if let Some(body) = &a.body {
1255 self.check_unused_delims_expr(
1256 cx,
1257 body,
1258 UnusedDelimsCtx::MatchArmExpr,
1259 false,
1260 None,
1261 None,
1262 true,
1263 );
1264 }
1265 }
1266 }
1267 _ => {}
1268 }
1269
1270 <Self as UnusedDelimLint>::check_expr(self, cx, e)
1271 }
1272
1273 fn check_expr_post(&mut self, _cx: &EarlyContext<'_>, e: &ast::Expr) {
1274 if let Some(ty_id) = self.cast_followed_by_lt(e) {
1275 let id = self
1276 .parens_in_cast_in_lt
1277 .pop()
1278 .expect("check_expr and check_expr_post must balance");
1279 assert_eq!(
1280 id, ty_id,
1281 "check_expr, check_ty, and check_expr_post are called, in that order, by the visitor"
1282 );
1283 }
1284 }
1285
1286 fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
1287 use ast::Mutability;
1288 use ast::PatKind::*;
1289 let keep_space = (false, false);
1290 match &p.kind {
1291 Paren(_)
1293 | Missing | Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None)
1295 | Path(..) | Err(_) => {},
1296 TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
1298 self.check_unused_parens_pat(cx, p, false, false, keep_space);
1299 },
1300 Struct(_, _, fps, _) => for f in fps {
1301 self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space);
1302 },
1303 Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
1305 Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
1308 }
1309 }
1310
1311 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1312 if let StmtKind::Let(ref local) = s.kind {
1313 self.check_unused_parens_pat(cx, &local.pat, true, false, (true, false));
1314 }
1315
1316 <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1317 }
1318
1319 fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) {
1320 self.check_unused_parens_pat(cx, ¶m.pat, true, false, (false, false));
1321 }
1322
1323 fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
1324 self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false));
1325 }
1326
1327 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1328 if let ast::TyKind::Paren(_) = ty.kind
1329 && Some(&ty.id) == self.parens_in_cast_in_lt.last()
1330 {
1331 return;
1332 }
1333 match &ty.kind {
1334 ast::TyKind::Array(_, len) => {
1335 self.check_unused_delims_expr(
1336 cx,
1337 &len.value,
1338 UnusedDelimsCtx::ArrayLenExpr,
1339 false,
1340 None,
1341 None,
1342 false,
1343 );
1344 }
1345 ast::TyKind::Paren(r) => {
1346 let unused_parens = match &r.kind {
1347 ast::TyKind::ImplTrait(_, bounds) | ast::TyKind::TraitObject(bounds, _) => {
1348 match self.in_no_bounds_pos.get(&ty.id) {
1349 Some(NoBoundsException::None) => false,
1350 Some(NoBoundsException::OneBound) => bounds.len() <= 1,
1351 None => true,
1352 }
1353 }
1354 ast::TyKind::FnPtr(b) => {
1355 !self.with_self_ty_parens || b.generic_params.is_empty()
1356 }
1357 _ => true,
1358 };
1359
1360 if unused_parens {
1361 let spans = (!ty.span.from_expansion())
1362 .then(|| {
1363 r.span
1364 .find_ancestor_inside(ty.span)
1365 .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
1366 })
1367 .flatten();
1368
1369 self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false);
1370 }
1371
1372 self.with_self_ty_parens = false;
1373 }
1374 ast::TyKind::Ref(_, mut_ty) | ast::TyKind::Ptr(mut_ty) => {
1375 let own_constraint = self.in_no_bounds_pos.get(&ty.id);
1378 let constraint = match own_constraint {
1379 Some(NoBoundsException::None) => NoBoundsException::None,
1380 Some(NoBoundsException::OneBound) => NoBoundsException::OneBound,
1381 None => NoBoundsException::OneBound,
1382 };
1383 self.in_no_bounds_pos.insert(mut_ty.ty.id, constraint);
1384 }
1385 ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
1386 for i in 0..bounds.len() {
1387 let is_last = i == bounds.len() - 1;
1388
1389 if let ast::GenericBound::Trait(poly_trait_ref) = &bounds[i] {
1390 let fn_with_explicit_ret_ty = if let [.., segment] =
1391 &*poly_trait_ref.trait_ref.path.segments
1392 && let Some(args) = segment.args.as_ref()
1393 && let ast::GenericArgs::Parenthesized(paren_args) = &**args
1394 && let ast::FnRetTy::Ty(ret_ty) = &paren_args.output
1395 {
1396 self.in_no_bounds_pos.insert(
1397 ret_ty.id,
1398 if is_last {
1399 NoBoundsException::OneBound
1400 } else {
1401 NoBoundsException::None
1402 },
1403 );
1404
1405 true
1406 } else {
1407 false
1408 };
1409
1410 let dyn2015_exception = cx.sess().psess.edition == Edition2015
1415 && matches!(ty.kind, ast::TyKind::TraitObject(..))
1416 && i == 0
1417 && poly_trait_ref
1418 .trait_ref
1419 .path
1420 .segments
1421 .first()
1422 .map(|s| s.ident.name == kw::PathRoot)
1423 .unwrap_or(false);
1424
1425 if let ast::Parens::Yes = poly_trait_ref.parens
1426 && (is_last || !fn_with_explicit_ret_ty)
1427 && !dyn2015_exception
1428 {
1429 let s = poly_trait_ref.span;
1430 let spans = (!s.from_expansion()).then(|| {
1431 (
1432 s.with_hi(s.lo() + rustc_span::BytePos(1)),
1433 s.with_lo(s.hi() - rustc_span::BytePos(1)),
1434 )
1435 });
1436
1437 self.emit_unused_delims(
1438 cx,
1439 poly_trait_ref.span,
1440 spans,
1441 "type",
1442 (false, false),
1443 false,
1444 );
1445 }
1446 }
1447 }
1448 }
1449 _ => {}
1450 }
1451 }
1452
1453 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1454 <Self as UnusedDelimLint>::check_item(self, cx, item)
1455 }
1456
1457 fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Item) {
1458 self.in_no_bounds_pos.clear();
1459 }
1460
1461 fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) {
1462 use rustc_ast::{WhereBoundPredicate, WherePredicateKind};
1463 if let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
1464 bounded_ty,
1465 bound_generic_params,
1466 ..
1467 }) = &pred.kind
1468 && let ast::TyKind::Paren(_) = &bounded_ty.kind
1469 && bound_generic_params.is_empty()
1470 {
1471 self.with_self_ty_parens = true;
1472 }
1473 }
1474
1475 fn exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate) {
1476 assert!(!self.with_self_ty_parens);
1477 }
1478}
1479
1480declare_lint! {
1481 pub(super) UNUSED_BRACES,
1499 Warn,
1500 "unnecessary braces around an expression"
1501}
1502
1503declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]);
1504
1505impl UnusedDelimLint for UnusedBraces {
1506 const DELIM_STR: &'static str = "braces";
1507
1508 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = false;
1509
1510 fn lint(&self) -> &'static Lint {
1511 UNUSED_BRACES
1512 }
1513
1514 fn check_unused_delims_expr(
1515 &self,
1516 cx: &EarlyContext<'_>,
1517 value: &ast::Expr,
1518 ctx: UnusedDelimsCtx,
1519 followed_by_block: bool,
1520 left_pos: Option<BytePos>,
1521 right_pos: Option<BytePos>,
1522 is_kw: bool,
1523 ) {
1524 match value.kind {
1525 ast::ExprKind::Block(ref inner, None)
1526 if inner.rules == ast::BlockCheckMode::Default =>
1527 {
1528 if let [stmt] = inner.stmts.as_slice()
1553 && let ast::StmtKind::Expr(ref expr) = stmt.kind
1554 && !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
1555 && (ctx != UnusedDelimsCtx::AnonConst
1556 || (matches!(expr.kind, ast::ExprKind::Lit(_))
1557 && !expr.span.from_expansion()))
1558 && ctx != UnusedDelimsCtx::ClosureBody
1559 && !cx.sess().source_map().is_multiline(value.span)
1560 && value.attrs.is_empty()
1561 && !value.span.from_expansion()
1562 && !inner.span.from_expansion()
1563 {
1564 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1565 }
1566 }
1567 ast::ExprKind::Let(_, ref expr, _, _) => {
1568 self.check_unused_delims_expr(
1569 cx,
1570 expr,
1571 UnusedDelimsCtx::LetScrutineeExpr,
1572 followed_by_block,
1573 None,
1574 None,
1575 false,
1576 );
1577 }
1578 _ => {}
1579 }
1580 }
1581}
1582
1583impl EarlyLintPass for UnusedBraces {
1584 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1585 <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1586 }
1587
1588 #[inline]
1589 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1590 <Self as UnusedDelimLint>::check_expr(self, cx, e);
1591
1592 if let ExprKind::Repeat(_, ref anon_const) = e.kind {
1593 self.check_unused_delims_expr(
1594 cx,
1595 &anon_const.value,
1596 UnusedDelimsCtx::AnonConst,
1597 false,
1598 None,
1599 None,
1600 false,
1601 );
1602 }
1603 }
1604
1605 fn check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg) {
1606 if let ast::GenericArg::Const(ct) = arg {
1607 self.check_unused_delims_expr(
1608 cx,
1609 &ct.value,
1610 UnusedDelimsCtx::AnonConst,
1611 false,
1612 None,
1613 None,
1614 false,
1615 );
1616 }
1617 }
1618
1619 fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &ast::Variant) {
1620 if let Some(anon_const) = &v.disr_expr {
1621 self.check_unused_delims_expr(
1622 cx,
1623 &anon_const.value,
1624 UnusedDelimsCtx::AnonConst,
1625 false,
1626 None,
1627 None,
1628 false,
1629 );
1630 }
1631 }
1632
1633 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1634 match ty.kind {
1635 ast::TyKind::Array(_, ref len) => {
1636 self.check_unused_delims_expr(
1637 cx,
1638 &len.value,
1639 UnusedDelimsCtx::ArrayLenExpr,
1640 false,
1641 None,
1642 None,
1643 false,
1644 );
1645 }
1646
1647 ast::TyKind::Typeof(ref anon_const) => {
1648 self.check_unused_delims_expr(
1649 cx,
1650 &anon_const.value,
1651 UnusedDelimsCtx::AnonConst,
1652 false,
1653 None,
1654 None,
1655 false,
1656 );
1657 }
1658
1659 _ => {}
1660 }
1661 }
1662
1663 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1664 <Self as UnusedDelimLint>::check_item(self, cx, item)
1665 }
1666}
1667
1668declare_lint! {
1669 UNUSED_IMPORT_BRACES,
1694 Allow,
1695 "unnecessary braces around an imported item"
1696}
1697
1698declare_lint_pass!(UnusedImportBraces => [UNUSED_IMPORT_BRACES]);
1699
1700impl UnusedImportBraces {
1701 fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) {
1702 if let ast::UseTreeKind::Nested { ref items, .. } = use_tree.kind {
1703 for (tree, _) in items {
1705 self.check_use_tree(cx, tree, item);
1706 }
1707
1708 let [(tree, _)] = items.as_slice() else { return };
1710
1711 let node_name = match tree.kind {
1713 ast::UseTreeKind::Simple(rename) => {
1714 let orig_ident = tree.prefix.segments.last().unwrap().ident;
1715 if orig_ident.name == kw::SelfLower {
1716 return;
1717 }
1718 rename.unwrap_or(orig_ident).name
1719 }
1720 ast::UseTreeKind::Glob => sym::asterisk,
1721 ast::UseTreeKind::Nested { .. } => return,
1722 };
1723
1724 cx.emit_span_lint(
1725 UNUSED_IMPORT_BRACES,
1726 item.span,
1727 UnusedImportBracesDiag { node: node_name },
1728 );
1729 }
1730 }
1731}
1732
1733impl EarlyLintPass for UnusedImportBraces {
1734 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1735 if let ast::ItemKind::Use(ref use_tree) = item.kind {
1736 self.check_use_tree(cx, use_tree, item);
1737 }
1738 }
1739}
1740
1741declare_lint! {
1742 pub(super) UNUSED_ALLOCATION,
1761 Warn,
1762 "detects unnecessary allocations that can be eliminated"
1763}
1764
1765declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]);
1766
1767impl<'tcx> LateLintPass<'tcx> for UnusedAllocation {
1768 fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
1769 match e.kind {
1770 hir::ExprKind::Call(path_expr, [_])
1771 if let hir::ExprKind::Path(qpath) = &path_expr.kind
1772 && let Some(did) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()
1773 && cx.tcx.is_diagnostic_item(sym::box_new, did) => {}
1774 _ => return,
1775 }
1776
1777 for adj in cx.typeck_results().expr_adjustments(e) {
1778 if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(m)) = adj.kind {
1779 match m {
1780 adjustment::AutoBorrowMutability::Not => {
1781 cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag);
1782 }
1783 adjustment::AutoBorrowMutability::Mut { .. } => {
1784 cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag);
1785 }
1786 };
1787 }
1788 }
1789 }
1790}