1use std::iter;
2
3use rustc_ast as ast;
4use rustc_ast::util::{classify, parser};
5use rustc_ast::{ExprKind, StmtKind};
6use rustc_errors::{MultiSpan, pluralize};
7use rustc_hir::def::{DefKind, Res};
8use rustc_hir::def_id::DefId;
9use rustc_hir::{self as hir, LangItem};
10use rustc_infer::traits::util::elaborate;
11use rustc_middle::ty::{self, Ty, adjustment};
12use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
13use rustc_span::{BytePos, Span, Symbol, kw, sym};
14use tracing::instrument;
15
16use crate::lints::{
17 PathStatementDrop, PathStatementDropSub, PathStatementNoEffect, UnusedAllocationDiag,
18 UnusedAllocationMutDiag, UnusedClosure, UnusedCoroutine, UnusedDef, UnusedDefSuggestion,
19 UnusedDelim, UnusedDelimSuggestion, UnusedImportBracesDiag, UnusedOp, UnusedOpSuggestion,
20 UnusedResult,
21};
22use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Lint, LintContext};
23
24declare_lint! {
25 pub UNUSED_MUST_USE,
49 Warn,
50 "unused result of a type flagged as `#[must_use]`",
51 report_in_external_macro
52}
53
54declare_lint! {
55 pub UNUSED_RESULTS,
89 Allow,
90 "unused result of an expression in a statement"
91}
92
93declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]);
94
95impl<'tcx> LateLintPass<'tcx> for UnusedResults {
96 fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
97 let hir::StmtKind::Semi(mut expr) = s.kind else {
98 return;
99 };
100
101 let mut expr_is_from_block = false;
102 while let hir::ExprKind::Block(blk, ..) = expr.kind
103 && let hir::Block { expr: Some(e), .. } = blk
104 {
105 expr = e;
106 expr_is_from_block = true;
107 }
108
109 if let hir::ExprKind::Ret(..) = expr.kind {
110 return;
111 }
112
113 if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind
114 && let ty = cx.typeck_results().expr_ty(await_expr)
115 && let ty::Alias(ty::Opaque, ty::AliasTy { def_id: future_def_id, .. }) = ty.kind()
116 && cx.tcx.ty_is_opaque_future(ty)
117 && let async_fn_def_id = cx.tcx.parent(*future_def_id)
118 && matches!(cx.tcx.def_kind(async_fn_def_id), DefKind::Fn | DefKind::AssocFn)
119 && cx.tcx.asyncness(async_fn_def_id).is_async()
121 && check_must_use_def(
122 cx,
123 async_fn_def_id,
124 expr.span,
125 "output of future returned by ",
126 "",
127 expr_is_from_block,
128 )
129 {
130 return;
133 }
134
135 let ty = cx.typeck_results().expr_ty(expr);
136
137 let must_use_result = is_ty_must_use(cx, ty, expr, expr.span);
138 let type_lint_emitted_or_suppressed = match must_use_result {
139 Some(path) => {
140 emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block);
141 true
142 }
143 None => false,
144 };
145
146 let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block);
147
148 if !fn_warned && type_lint_emitted_or_suppressed {
149 return;
152 }
153
154 let must_use_op = match expr.kind {
155 hir::ExprKind::Binary(bin_op, ..) => match bin_op.node {
159 hir::BinOpKind::Eq
160 | hir::BinOpKind::Lt
161 | hir::BinOpKind::Le
162 | hir::BinOpKind::Ne
163 | hir::BinOpKind::Ge
164 | hir::BinOpKind::Gt => Some("comparison"),
165 hir::BinOpKind::Add
166 | hir::BinOpKind::Sub
167 | hir::BinOpKind::Div
168 | hir::BinOpKind::Mul
169 | hir::BinOpKind::Rem => Some("arithmetic operation"),
170 hir::BinOpKind::And | hir::BinOpKind::Or => Some("logical operation"),
171 hir::BinOpKind::BitXor
172 | hir::BinOpKind::BitAnd
173 | hir::BinOpKind::BitOr
174 | hir::BinOpKind::Shl
175 | hir::BinOpKind::Shr => Some("bitwise operation"),
176 },
177 hir::ExprKind::AddrOf(..) => Some("borrow"),
178 hir::ExprKind::OffsetOf(..) => Some("`offset_of` call"),
179 hir::ExprKind::Unary(..) => Some("unary operation"),
180 _ => None,
181 };
182
183 let mut op_warned = false;
184
185 if let Some(must_use_op) = must_use_op {
186 cx.emit_span_lint(
187 UNUSED_MUST_USE,
188 expr.span,
189 UnusedOp {
190 op: must_use_op,
191 label: expr.span,
192 suggestion: if expr_is_from_block {
193 UnusedOpSuggestion::BlockTailExpr {
194 before_span: expr.span.shrink_to_lo(),
195 after_span: expr.span.shrink_to_hi(),
196 }
197 } else {
198 UnusedOpSuggestion::NormalExpr { span: expr.span.shrink_to_lo() }
199 },
200 },
201 );
202 op_warned = true;
203 }
204
205 if !(type_lint_emitted_or_suppressed || fn_warned || op_warned) {
206 cx.emit_span_lint(UNUSED_RESULTS, s.span, UnusedResult { ty });
207 }
208
209 fn check_fn_must_use(
210 cx: &LateContext<'_>,
211 expr: &hir::Expr<'_>,
212 expr_is_from_block: bool,
213 ) -> bool {
214 let maybe_def_id = match expr.kind {
215 hir::ExprKind::Call(callee, _) => {
216 match callee.kind {
217 hir::ExprKind::Path(ref qpath) => {
218 match cx.qpath_res(qpath, callee.hir_id) {
219 Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => Some(def_id),
220 _ => None,
223 }
224 }
225 _ => None,
226 }
227 }
228 hir::ExprKind::MethodCall(..) => {
229 cx.typeck_results().type_dependent_def_id(expr.hir_id)
230 }
231 _ => None,
232 };
233 if let Some(def_id) = maybe_def_id {
234 check_must_use_def(
235 cx,
236 def_id,
237 expr.span,
238 "return value of ",
239 "",
240 expr_is_from_block,
241 )
242 } else {
243 false
244 }
245 }
246
247 #[derive(Debug)]
249 enum MustUsePath {
250 Suppressed,
252 Def(Span, DefId, Option<Symbol>),
254 Boxed(Box<Self>),
255 Pinned(Box<Self>),
256 Opaque(Box<Self>),
257 TraitObject(Box<Self>),
258 TupleElement(Vec<(usize, Self)>),
259 Array(Box<Self>, u64),
260 Closure(Span),
262 Coroutine(Span),
264 }
265
266 #[instrument(skip(cx, expr), level = "debug", ret)]
267 fn is_ty_must_use<'tcx>(
268 cx: &LateContext<'tcx>,
269 ty: Ty<'tcx>,
270 expr: &hir::Expr<'_>,
271 span: Span,
272 ) -> Option<MustUsePath> {
273 if ty.is_unit()
274 || !ty.is_inhabited_from(
275 cx.tcx,
276 cx.tcx.parent_module(expr.hir_id).to_def_id(),
277 cx.typing_env(),
278 )
279 {
280 return Some(MustUsePath::Suppressed);
281 }
282
283 match *ty.kind() {
284 ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => {
285 is_ty_must_use(cx, boxed, expr, span)
286 .map(|inner| MustUsePath::Boxed(Box::new(inner)))
287 }
288 ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
289 let pinned_ty = args.type_at(0);
290 is_ty_must_use(cx, pinned_ty, expr, span)
291 .map(|inner| MustUsePath::Pinned(Box::new(inner)))
292 }
293 ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
294 ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => {
295 elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied())
296 .filter_only_self()
298 .find_map(|(pred, _span)| {
299 if let ty::ClauseKind::Trait(ref poly_trait_predicate) =
301 pred.kind().skip_binder()
302 {
303 let def_id = poly_trait_predicate.trait_ref.def_id;
304
305 is_def_must_use(cx, def_id, span)
306 } else {
307 None
308 }
309 })
310 .map(|inner| MustUsePath::Opaque(Box::new(inner)))
311 }
312 ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| {
313 if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
314 {
315 let def_id = trait_ref.def_id;
316 is_def_must_use(cx, def_id, span)
317 .map(|inner| MustUsePath::TraitObject(Box::new(inner)))
318 } else {
319 None
320 }
321 }),
322 ty::Tuple(tys) => {
323 let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind {
324 debug_assert_eq!(elem_exprs.len(), tys.len());
325 elem_exprs
326 } else {
327 &[]
328 };
329
330 let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr));
332
333 let nested_must_use = tys
334 .iter()
335 .zip(elem_exprs)
336 .enumerate()
337 .filter_map(|(i, (ty, expr))| {
338 is_ty_must_use(cx, ty, expr, expr.span).map(|path| (i, path))
339 })
340 .collect::<Vec<_>>();
341
342 if !nested_must_use.is_empty() {
343 Some(MustUsePath::TupleElement(nested_must_use))
344 } else {
345 None
346 }
347 }
348 ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) {
349 Some(0) | None => None,
351 Some(len) => is_ty_must_use(cx, ty, expr, span)
353 .map(|inner| MustUsePath::Array(Box::new(inner), len)),
354 },
355 ty::Closure(..) | ty::CoroutineClosure(..) => Some(MustUsePath::Closure(span)),
356 ty::Coroutine(def_id, ..) => {
357 let must_use = if cx.tcx.coroutine_is_async(def_id) {
359 let def_id = cx.tcx.lang_items().future_trait()?;
360 is_def_must_use(cx, def_id, span)
361 .map(|inner| MustUsePath::Opaque(Box::new(inner)))
362 } else {
363 None
364 };
365 must_use.or(Some(MustUsePath::Coroutine(span)))
366 }
367 _ => None,
368 }
369 }
370
371 fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option<MustUsePath> {
372 if let Some(attr) = cx.tcx.get_attr(def_id, sym::must_use) {
373 let reason = attr.value_str();
375 Some(MustUsePath::Def(span, def_id, reason))
376 } else {
377 None
378 }
379 }
380
381 fn check_must_use_def(
384 cx: &LateContext<'_>,
385 def_id: DefId,
386 span: Span,
387 descr_pre_path: &str,
388 descr_post_path: &str,
389 expr_is_from_block: bool,
390 ) -> bool {
391 is_def_must_use(cx, def_id, span)
392 .map(|must_use_path| {
393 emit_must_use_untranslated(
394 cx,
395 &must_use_path,
396 descr_pre_path,
397 descr_post_path,
398 1,
399 false,
400 expr_is_from_block,
401 )
402 })
403 .is_some()
404 }
405
406 #[instrument(skip(cx), level = "debug")]
407 fn emit_must_use_untranslated(
408 cx: &LateContext<'_>,
409 path: &MustUsePath,
410 descr_pre: &str,
411 descr_post: &str,
412 plural_len: usize,
413 is_inner: bool,
414 expr_is_from_block: bool,
415 ) {
416 let plural_suffix = pluralize!(plural_len);
417
418 match path {
419 MustUsePath::Suppressed => {}
420 MustUsePath::Boxed(path) => {
421 let descr_pre = &format!("{descr_pre}boxed ");
422 emit_must_use_untranslated(
423 cx,
424 path,
425 descr_pre,
426 descr_post,
427 plural_len,
428 true,
429 expr_is_from_block,
430 );
431 }
432 MustUsePath::Pinned(path) => {
433 let descr_pre = &format!("{descr_pre}pinned ");
434 emit_must_use_untranslated(
435 cx,
436 path,
437 descr_pre,
438 descr_post,
439 plural_len,
440 true,
441 expr_is_from_block,
442 );
443 }
444 MustUsePath::Opaque(path) => {
445 let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of ");
446 emit_must_use_untranslated(
447 cx,
448 path,
449 descr_pre,
450 descr_post,
451 plural_len,
452 true,
453 expr_is_from_block,
454 );
455 }
456 MustUsePath::TraitObject(path) => {
457 let descr_post = &format!(" trait object{plural_suffix}{descr_post}");
458 emit_must_use_untranslated(
459 cx,
460 path,
461 descr_pre,
462 descr_post,
463 plural_len,
464 true,
465 expr_is_from_block,
466 );
467 }
468 MustUsePath::TupleElement(elems) => {
469 for (index, path) in elems {
470 let descr_post = &format!(" in tuple element {index}");
471 emit_must_use_untranslated(
472 cx,
473 path,
474 descr_pre,
475 descr_post,
476 plural_len,
477 true,
478 expr_is_from_block,
479 );
480 }
481 }
482 MustUsePath::Array(path, len) => {
483 let descr_pre = &format!("{descr_pre}array{plural_suffix} of ");
484 emit_must_use_untranslated(
485 cx,
486 path,
487 descr_pre,
488 descr_post,
489 plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)),
490 true,
491 expr_is_from_block,
492 );
493 }
494 MustUsePath::Closure(span) => {
495 cx.emit_span_lint(
496 UNUSED_MUST_USE,
497 *span,
498 UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post },
499 );
500 }
501 MustUsePath::Coroutine(span) => {
502 cx.emit_span_lint(
503 UNUSED_MUST_USE,
504 *span,
505 UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post },
506 );
507 }
508 MustUsePath::Def(span, def_id, reason) => {
509 cx.emit_span_lint(
510 UNUSED_MUST_USE,
511 *span,
512 UnusedDef {
513 pre: descr_pre,
514 post: descr_post,
515 cx,
516 def_id: *def_id,
517 note: *reason,
518 suggestion: (!is_inner).then_some(if expr_is_from_block {
519 UnusedDefSuggestion::BlockTailExpr {
520 before_span: span.shrink_to_lo(),
521 after_span: span.shrink_to_hi(),
522 }
523 } else {
524 UnusedDefSuggestion::NormalExpr { span: span.shrink_to_lo() }
525 }),
526 },
527 );
528 }
529 }
530 }
531 }
532}
533
534declare_lint! {
535 pub PATH_STATEMENTS,
551 Warn,
552 "path statements with no effect"
553}
554
555declare_lint_pass!(PathStatements => [PATH_STATEMENTS]);
556
557impl<'tcx> LateLintPass<'tcx> for PathStatements {
558 fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
559 if let hir::StmtKind::Semi(expr) = s.kind {
560 if let hir::ExprKind::Path(_) = expr.kind {
561 let ty = cx.typeck_results().expr_ty(expr);
562 if ty.needs_drop(cx.tcx, cx.typing_env()) {
563 let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span)
564 {
565 PathStatementDropSub::Suggestion { span: s.span, snippet }
566 } else {
567 PathStatementDropSub::Help { span: s.span }
568 };
569 cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub })
570 } else {
571 cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect);
572 }
573 }
574 }
575 }
576}
577
578#[derive(Copy, Clone, Debug, PartialEq, Eq)]
579enum UnusedDelimsCtx {
580 FunctionArg,
581 MethodArg,
582 AssignedValue,
583 AssignedValueLetElse,
584 IfCond,
585 WhileCond,
586 ForIterExpr,
587 MatchScrutineeExpr,
588 ReturnValue,
589 BlockRetValue,
590 BreakValue,
591 LetScrutineeExpr,
592 ArrayLenExpr,
593 AnonConst,
594 MatchArmExpr,
595 IndexExpr,
596}
597
598impl From<UnusedDelimsCtx> for &'static str {
599 fn from(ctx: UnusedDelimsCtx) -> &'static str {
600 match ctx {
601 UnusedDelimsCtx::FunctionArg => "function argument",
602 UnusedDelimsCtx::MethodArg => "method argument",
603 UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => {
604 "assigned value"
605 }
606 UnusedDelimsCtx::IfCond => "`if` condition",
607 UnusedDelimsCtx::WhileCond => "`while` condition",
608 UnusedDelimsCtx::ForIterExpr => "`for` iterator expression",
609 UnusedDelimsCtx::MatchScrutineeExpr => "`match` scrutinee expression",
610 UnusedDelimsCtx::ReturnValue => "`return` value",
611 UnusedDelimsCtx::BlockRetValue => "block return value",
612 UnusedDelimsCtx::BreakValue => "`break` value",
613 UnusedDelimsCtx::LetScrutineeExpr => "`let` scrutinee expression",
614 UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression",
615 UnusedDelimsCtx::MatchArmExpr => "match arm expression",
616 UnusedDelimsCtx::IndexExpr => "index expression",
617 }
618 }
619}
620
621trait UnusedDelimLint {
623 const DELIM_STR: &'static str;
624
625 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool;
637
638 fn lint(&self) -> &'static Lint;
640
641 fn check_unused_delims_expr(
642 &self,
643 cx: &EarlyContext<'_>,
644 value: &ast::Expr,
645 ctx: UnusedDelimsCtx,
646 followed_by_block: bool,
647 left_pos: Option<BytePos>,
648 right_pos: Option<BytePos>,
649 is_kw: bool,
650 );
651
652 fn is_expr_delims_necessary(
653 inner: &ast::Expr,
654 ctx: UnusedDelimsCtx,
655 followed_by_block: bool,
656 ) -> bool {
657 let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
658
659 if followed_by_else {
660 match inner.kind {
661 ast::ExprKind::Binary(op, ..) if op.node.is_lazy() => return true,
662 _ if classify::expr_trailing_brace(inner).is_some() => return true,
663 _ => {}
664 }
665 }
666
667 if let ast::ExprKind::Range(..) = inner.kind
669 && matches!(ctx, UnusedDelimsCtx::LetScrutineeExpr)
670 {
671 return true;
672 }
673
674 if matches!(inner.kind, ast::ExprKind::AddrOf(ast::BorrowKind::Raw, ..)) {
678 return true;
679 }
680
681 {
711 let mut innermost = inner;
712 loop {
713 innermost = match &innermost.kind {
714 ExprKind::Binary(_op, lhs, _rhs) => lhs,
715 ExprKind::Call(fn_, _params) => fn_,
716 ExprKind::Cast(expr, _ty) => expr,
717 ExprKind::Type(expr, _ty) => expr,
718 ExprKind::Index(base, _subscript, _) => base,
719 _ => break,
720 };
721 if !classify::expr_requires_semi_to_be_stmt(innermost) {
722 return true;
723 }
724 }
725 }
726
727 if !followed_by_block {
730 return false;
731 }
732
733 {
735 let mut innermost = inner;
736 loop {
737 innermost = match &innermost.kind {
738 ExprKind::AddrOf(_, _, expr) => expr,
739 _ => {
740 if parser::contains_exterior_struct_lit(innermost) {
741 return true;
742 } else {
743 break;
744 }
745 }
746 }
747 }
748 }
749
750 let mut innermost = inner;
751 loop {
752 innermost = match &innermost.kind {
753 ExprKind::Unary(_op, expr) => expr,
754 ExprKind::Binary(_op, _lhs, rhs) => rhs,
755 ExprKind::AssignOp(_op, _lhs, rhs) => rhs,
756 ExprKind::Assign(_lhs, rhs, _span) => rhs,
757
758 ExprKind::Ret(_) | ExprKind::Yield(..) | ExprKind::Yeet(..) => return true,
759
760 ExprKind::Break(_label, None) => return false,
761 ExprKind::Break(_label, Some(break_expr)) => {
762 return matches!(break_expr.kind, ExprKind::Block(..));
763 }
764
765 ExprKind::Range(_lhs, Some(rhs), _limits) => {
766 return matches!(rhs.kind, ExprKind::Block(..));
767 }
768
769 _ => return parser::contains_exterior_struct_lit(inner),
770 }
771 }
772 }
773
774 fn emit_unused_delims_expr(
775 &self,
776 cx: &EarlyContext<'_>,
777 value: &ast::Expr,
778 ctx: UnusedDelimsCtx,
779 left_pos: Option<BytePos>,
780 right_pos: Option<BytePos>,
781 is_kw: bool,
782 ) {
783 let spans = match value.kind {
784 ast::ExprKind::Block(ref block, None) if let [stmt] = block.stmts.as_slice() => stmt
785 .span
786 .find_ancestor_inside(value.span)
787 .map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi()))),
788 ast::ExprKind::Paren(ref expr) => {
789 let expr_span_with_attrs =
792 if let Some(attr_lo) = expr.attrs.iter().map(|attr| attr.span.lo()).min() {
793 expr.span.with_lo(attr_lo)
794 } else {
795 expr.span
796 };
797 expr_span_with_attrs.find_ancestor_inside(value.span).map(|expr_span| {
798 (value.span.with_hi(expr_span.lo()), value.span.with_lo(expr_span.hi()))
799 })
800 }
801 _ => return,
802 };
803 let keep_space = (
804 left_pos.is_some_and(|s| s >= value.span.lo()),
805 right_pos.is_some_and(|s| s <= value.span.hi()),
806 );
807 self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space, is_kw);
808 }
809
810 fn emit_unused_delims(
811 &self,
812 cx: &EarlyContext<'_>,
813 value_span: Span,
814 spans: Option<(Span, Span)>,
815 msg: &str,
816 keep_space: (bool, bool),
817 is_kw: bool,
818 ) {
819 let primary_span = if let Some((lo, hi)) = spans {
820 if hi.is_empty() {
821 return;
823 }
824 MultiSpan::from(vec![lo, hi])
825 } else {
826 MultiSpan::from(value_span)
827 };
828 let suggestion = spans.map(|(lo, hi)| {
829 let sm = cx.sess().source_map();
830 let lo_replace = if (keep_space.0 || is_kw)
831 && let Ok(snip) = sm.span_to_prev_source(lo)
832 && !snip.ends_with(' ')
833 {
834 " "
835 } else {
836 ""
837 };
838
839 let hi_replace = if keep_space.1
840 && let Ok(snip) = sm.span_to_next_source(hi)
841 && !snip.starts_with(' ')
842 {
843 " "
844 } else {
845 ""
846 };
847 UnusedDelimSuggestion {
848 start_span: lo,
849 start_replace: lo_replace,
850 end_span: hi,
851 end_replace: hi_replace,
852 }
853 });
854 cx.emit_span_lint(
855 self.lint(),
856 primary_span,
857 UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion },
858 );
859 }
860
861 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
862 use rustc_ast::ExprKind::*;
863 let (value, ctx, followed_by_block, left_pos, right_pos, is_kw) = match e.kind {
864 If(ref cond, ref block, _)
866 if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
867 {
868 let left = e.span.lo() + rustc_span::BytePos(2);
869 let right = block.span.lo();
870 (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right), true)
871 }
872
873 While(ref cond, ref block, ..)
875 if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
876 {
877 let left = e.span.lo() + rustc_span::BytePos(5);
878 let right = block.span.lo();
879 (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right), true)
880 }
881
882 ForLoop { ref iter, ref body, .. } => {
883 (iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true)
884 }
885
886 Match(ref head, _, ast::MatchKind::Prefix)
887 if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
888 {
889 let left = e.span.lo() + rustc_span::BytePos(5);
890 (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true)
891 }
892
893 Ret(Some(ref value)) => {
894 let left = e.span.lo() + rustc_span::BytePos(3);
895 (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true)
896 }
897
898 Break(_, Some(ref value)) => {
899 (value, UnusedDelimsCtx::BreakValue, false, None, None, true)
900 }
901
902 Index(_, ref value, _) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false),
903
904 Assign(_, ref value, _) | AssignOp(.., ref value) => {
905 (value, UnusedDelimsCtx::AssignedValue, false, None, None, false)
906 }
907 ref call_or_other => {
909 let (args_to_check, ctx) = match *call_or_other {
910 Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg),
911 MethodCall(ref call) => (&call.args[..], UnusedDelimsCtx::MethodArg),
912 _ => {
914 return;
915 }
916 };
917 if e.span.ctxt().outer_expn_data().call_site.from_expansion() {
922 return;
923 }
924 for arg in args_to_check {
925 self.check_unused_delims_expr(cx, arg, ctx, false, None, None, false);
926 }
927 return;
928 }
929 };
930 self.check_unused_delims_expr(
931 cx,
932 value,
933 ctx,
934 followed_by_block,
935 left_pos,
936 right_pos,
937 is_kw,
938 );
939 }
940
941 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
942 match s.kind {
943 StmtKind::Let(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
944 if let Some((init, els)) = local.kind.init_else_opt() {
945 if els.is_some()
946 && let ExprKind::Paren(paren) = &init.kind
947 && !init.span.eq_ctxt(paren.span)
948 {
949 return;
960 }
961 let ctx = match els {
962 None => UnusedDelimsCtx::AssignedValue,
963 Some(_) => UnusedDelimsCtx::AssignedValueLetElse,
964 };
965 self.check_unused_delims_expr(cx, init, ctx, false, None, None, false);
966 }
967 }
968 StmtKind::Expr(ref expr) => {
969 self.check_unused_delims_expr(
970 cx,
971 expr,
972 UnusedDelimsCtx::BlockRetValue,
973 false,
974 None,
975 None,
976 false,
977 );
978 }
979 _ => {}
980 }
981 }
982
983 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
984 use ast::ItemKind::*;
985
986 if let Const(box ast::ConstItem { expr: Some(expr), .. })
987 | Static(box ast::StaticItem { expr: Some(expr), .. }) = &item.kind
988 {
989 self.check_unused_delims_expr(
990 cx,
991 expr,
992 UnusedDelimsCtx::AssignedValue,
993 false,
994 None,
995 None,
996 false,
997 );
998 }
999 }
1000}
1001
1002declare_lint! {
1003 pub(super) UNUSED_PARENS,
1019 Warn,
1020 "`if`, `match`, `while` and `return` do not need parentheses"
1021}
1022
1023#[derive(Default)]
1024pub(crate) struct UnusedParens {
1025 with_self_ty_parens: bool,
1026 parens_in_cast_in_lt: Vec<ast::NodeId>,
1029}
1030
1031impl_lint_pass!(UnusedParens => [UNUSED_PARENS]);
1032
1033impl UnusedDelimLint for UnusedParens {
1034 const DELIM_STR: &'static str = "parentheses";
1035
1036 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = true;
1037
1038 fn lint(&self) -> &'static Lint {
1039 UNUSED_PARENS
1040 }
1041
1042 fn check_unused_delims_expr(
1043 &self,
1044 cx: &EarlyContext<'_>,
1045 value: &ast::Expr,
1046 ctx: UnusedDelimsCtx,
1047 followed_by_block: bool,
1048 left_pos: Option<BytePos>,
1049 right_pos: Option<BytePos>,
1050 is_kw: bool,
1051 ) {
1052 match value.kind {
1053 ast::ExprKind::Paren(ref inner) => {
1054 if !Self::is_expr_delims_necessary(inner, ctx, followed_by_block)
1055 && value.attrs.is_empty()
1056 && !value.span.from_expansion()
1057 && (ctx != UnusedDelimsCtx::LetScrutineeExpr
1058 || !matches!(inner.kind, ast::ExprKind::Binary(
1059 rustc_span::source_map::Spanned { node, .. },
1060 _,
1061 _,
1062 ) if node.is_lazy()))
1063 && !((ctx == UnusedDelimsCtx::ReturnValue
1064 || ctx == UnusedDelimsCtx::BreakValue)
1065 && matches!(inner.kind, ast::ExprKind::Assign(_, _, _)))
1066 {
1067 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1068 }
1069 }
1070 ast::ExprKind::Let(_, ref expr, _, _) => {
1071 self.check_unused_delims_expr(
1072 cx,
1073 expr,
1074 UnusedDelimsCtx::LetScrutineeExpr,
1075 followed_by_block,
1076 None,
1077 None,
1078 false,
1079 );
1080 }
1081 _ => {}
1082 }
1083 }
1084}
1085
1086impl UnusedParens {
1087 fn check_unused_parens_pat(
1088 &self,
1089 cx: &EarlyContext<'_>,
1090 value: &ast::Pat,
1091 avoid_or: bool,
1092 avoid_mut: bool,
1093 keep_space: (bool, bool),
1094 ) {
1095 use ast::{BindingMode, PatKind};
1096
1097 if let PatKind::Paren(inner) = &value.kind {
1098 match inner.kind {
1099 PatKind::Range(..) => return,
1104 PatKind::Or(..) if avoid_or => return,
1106 PatKind::Ident(BindingMode::MUT, ..) if avoid_mut => {
1108 return;
1109 }
1110 _ => {}
1112 }
1113 let spans = if !value.span.from_expansion() {
1114 inner
1115 .span
1116 .find_ancestor_inside(value.span)
1117 .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi())))
1118 } else {
1119 None
1120 };
1121 self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false);
1122 }
1123 }
1124
1125 fn cast_followed_by_lt(&self, expr: &ast::Expr) -> Option<ast::NodeId> {
1126 if let ExprKind::Binary(op, lhs, _rhs) = &expr.kind
1127 && (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl)
1128 {
1129 let mut cur = lhs;
1130 while let ExprKind::Binary(_, _, rhs) = &cur.kind {
1131 cur = rhs;
1132 }
1133
1134 if let ExprKind::Cast(_, ty) = &cur.kind
1135 && let ast::TyKind::Paren(_) = &ty.kind
1136 {
1137 return Some(ty.id);
1138 }
1139 }
1140 None
1141 }
1142}
1143
1144impl EarlyLintPass for UnusedParens {
1145 #[inline]
1146 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1147 if let Some(ty_id) = self.cast_followed_by_lt(e) {
1148 self.parens_in_cast_in_lt.push(ty_id);
1149 }
1150
1151 match e.kind {
1152 ExprKind::Let(ref pat, _, _, _) | ExprKind::ForLoop { ref pat, .. } => {
1153 self.check_unused_parens_pat(cx, pat, false, false, (true, true));
1154 }
1155 ExprKind::If(ref cond, ref block, ref else_)
1159 if matches!(cond.peel_parens().kind, ExprKind::Let(..)) =>
1160 {
1161 self.check_unused_delims_expr(
1162 cx,
1163 cond.peel_parens(),
1164 UnusedDelimsCtx::LetScrutineeExpr,
1165 true,
1166 None,
1167 None,
1168 true,
1169 );
1170 for stmt in &block.stmts {
1171 <Self as UnusedDelimLint>::check_stmt(self, cx, stmt);
1172 }
1173 if let Some(e) = else_ {
1174 <Self as UnusedDelimLint>::check_expr(self, cx, e);
1175 }
1176 return;
1177 }
1178 ExprKind::Match(ref _expr, ref arm, _) => {
1179 for a in arm {
1180 if let Some(body) = &a.body {
1181 self.check_unused_delims_expr(
1182 cx,
1183 body,
1184 UnusedDelimsCtx::MatchArmExpr,
1185 false,
1186 None,
1187 None,
1188 true,
1189 );
1190 }
1191 }
1192 }
1193 _ => {}
1194 }
1195
1196 <Self as UnusedDelimLint>::check_expr(self, cx, e)
1197 }
1198
1199 fn check_expr_post(&mut self, _cx: &EarlyContext<'_>, e: &ast::Expr) {
1200 if let Some(ty_id) = self.cast_followed_by_lt(e) {
1201 let id = self
1202 .parens_in_cast_in_lt
1203 .pop()
1204 .expect("check_expr and check_expr_post must balance");
1205 assert_eq!(
1206 id, ty_id,
1207 "check_expr, check_ty, and check_expr_post are called, in that order, by the visitor"
1208 );
1209 }
1210 }
1211
1212 fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
1213 use ast::Mutability;
1214 use ast::PatKind::*;
1215 let keep_space = (false, false);
1216 match &p.kind {
1217 Paren(_)
1219 | Missing | Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None)
1221 | Path(..) | Err(_) => {},
1222 TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
1224 self.check_unused_parens_pat(cx, p, false, false, keep_space);
1225 },
1226 Struct(_, _, fps, _) => for f in fps {
1227 self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space);
1228 },
1229 Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
1231 Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
1234 }
1235 }
1236
1237 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1238 if let StmtKind::Let(ref local) = s.kind {
1239 self.check_unused_parens_pat(cx, &local.pat, true, false, (true, false));
1240 }
1241
1242 <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1243 }
1244
1245 fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) {
1246 self.check_unused_parens_pat(cx, ¶m.pat, true, false, (false, false));
1247 }
1248
1249 fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
1250 self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false));
1251 }
1252
1253 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1254 if let ast::TyKind::Paren(_) = ty.kind
1255 && Some(&ty.id) == self.parens_in_cast_in_lt.last()
1256 {
1257 return;
1258 }
1259 match &ty.kind {
1260 ast::TyKind::Array(_, len) => {
1261 self.check_unused_delims_expr(
1262 cx,
1263 &len.value,
1264 UnusedDelimsCtx::ArrayLenExpr,
1265 false,
1266 None,
1267 None,
1268 false,
1269 );
1270 }
1271 ast::TyKind::Paren(r) => {
1272 match &r.kind {
1273 ast::TyKind::TraitObject(..) => {}
1274 ast::TyKind::BareFn(b)
1275 if self.with_self_ty_parens && b.generic_params.len() > 0 => {}
1276 ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {}
1277 _ => {
1278 let spans = if !ty.span.from_expansion() {
1279 r.span
1280 .find_ancestor_inside(ty.span)
1281 .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
1282 } else {
1283 None
1284 };
1285 self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false);
1286 }
1287 }
1288 self.with_self_ty_parens = false;
1289 }
1290 _ => {}
1291 }
1292 }
1293
1294 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1295 <Self as UnusedDelimLint>::check_item(self, cx, item)
1296 }
1297
1298 fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) {
1299 use rustc_ast::{WhereBoundPredicate, WherePredicateKind};
1300 if let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
1301 bounded_ty,
1302 bound_generic_params,
1303 ..
1304 }) = &pred.kind
1305 && let ast::TyKind::Paren(_) = &bounded_ty.kind
1306 && bound_generic_params.is_empty()
1307 {
1308 self.with_self_ty_parens = true;
1309 }
1310 }
1311
1312 fn exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate) {
1313 assert!(!self.with_self_ty_parens);
1314 }
1315}
1316
1317declare_lint! {
1318 pub(super) UNUSED_BRACES,
1336 Warn,
1337 "unnecessary braces around an expression"
1338}
1339
1340declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]);
1341
1342impl UnusedDelimLint for UnusedBraces {
1343 const DELIM_STR: &'static str = "braces";
1344
1345 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = false;
1346
1347 fn lint(&self) -> &'static Lint {
1348 UNUSED_BRACES
1349 }
1350
1351 fn check_unused_delims_expr(
1352 &self,
1353 cx: &EarlyContext<'_>,
1354 value: &ast::Expr,
1355 ctx: UnusedDelimsCtx,
1356 followed_by_block: bool,
1357 left_pos: Option<BytePos>,
1358 right_pos: Option<BytePos>,
1359 is_kw: bool,
1360 ) {
1361 match value.kind {
1362 ast::ExprKind::Block(ref inner, None)
1363 if inner.rules == ast::BlockCheckMode::Default =>
1364 {
1365 if let [stmt] = inner.stmts.as_slice() {
1390 if let ast::StmtKind::Expr(ref expr) = stmt.kind {
1391 if !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
1392 && (ctx != UnusedDelimsCtx::AnonConst
1393 || (matches!(expr.kind, ast::ExprKind::Lit(_))
1394 && !expr.span.from_expansion()))
1395 && !cx.sess().source_map().is_multiline(value.span)
1396 && value.attrs.is_empty()
1397 && !value.span.from_expansion()
1398 && !inner.span.from_expansion()
1399 {
1400 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1401 }
1402 }
1403 }
1404 }
1405 ast::ExprKind::Let(_, ref expr, _, _) => {
1406 self.check_unused_delims_expr(
1407 cx,
1408 expr,
1409 UnusedDelimsCtx::LetScrutineeExpr,
1410 followed_by_block,
1411 None,
1412 None,
1413 false,
1414 );
1415 }
1416 _ => {}
1417 }
1418 }
1419}
1420
1421impl EarlyLintPass for UnusedBraces {
1422 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1423 <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1424 }
1425
1426 #[inline]
1427 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1428 <Self as UnusedDelimLint>::check_expr(self, cx, e);
1429
1430 if let ExprKind::Repeat(_, ref anon_const) = e.kind {
1431 self.check_unused_delims_expr(
1432 cx,
1433 &anon_const.value,
1434 UnusedDelimsCtx::AnonConst,
1435 false,
1436 None,
1437 None,
1438 false,
1439 );
1440 }
1441 }
1442
1443 fn check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg) {
1444 if let ast::GenericArg::Const(ct) = arg {
1445 self.check_unused_delims_expr(
1446 cx,
1447 &ct.value,
1448 UnusedDelimsCtx::AnonConst,
1449 false,
1450 None,
1451 None,
1452 false,
1453 );
1454 }
1455 }
1456
1457 fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &ast::Variant) {
1458 if let Some(anon_const) = &v.disr_expr {
1459 self.check_unused_delims_expr(
1460 cx,
1461 &anon_const.value,
1462 UnusedDelimsCtx::AnonConst,
1463 false,
1464 None,
1465 None,
1466 false,
1467 );
1468 }
1469 }
1470
1471 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1472 match ty.kind {
1473 ast::TyKind::Array(_, ref len) => {
1474 self.check_unused_delims_expr(
1475 cx,
1476 &len.value,
1477 UnusedDelimsCtx::ArrayLenExpr,
1478 false,
1479 None,
1480 None,
1481 false,
1482 );
1483 }
1484
1485 ast::TyKind::Typeof(ref anon_const) => {
1486 self.check_unused_delims_expr(
1487 cx,
1488 &anon_const.value,
1489 UnusedDelimsCtx::AnonConst,
1490 false,
1491 None,
1492 None,
1493 false,
1494 );
1495 }
1496
1497 _ => {}
1498 }
1499 }
1500
1501 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1502 <Self as UnusedDelimLint>::check_item(self, cx, item)
1503 }
1504}
1505
1506declare_lint! {
1507 UNUSED_IMPORT_BRACES,
1532 Allow,
1533 "unnecessary braces around an imported item"
1534}
1535
1536declare_lint_pass!(UnusedImportBraces => [UNUSED_IMPORT_BRACES]);
1537
1538impl UnusedImportBraces {
1539 fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) {
1540 if let ast::UseTreeKind::Nested { ref items, .. } = use_tree.kind {
1541 for (tree, _) in items {
1543 self.check_use_tree(cx, tree, item);
1544 }
1545
1546 let [(tree, _)] = items.as_slice() else { return };
1548
1549 let node_name = match tree.kind {
1551 ast::UseTreeKind::Simple(rename) => {
1552 let orig_ident = tree.prefix.segments.last().unwrap().ident;
1553 if orig_ident.name == kw::SelfLower {
1554 return;
1555 }
1556 rename.unwrap_or(orig_ident).name
1557 }
1558 ast::UseTreeKind::Glob => sym::asterisk,
1559 ast::UseTreeKind::Nested { .. } => return,
1560 };
1561
1562 cx.emit_span_lint(
1563 UNUSED_IMPORT_BRACES,
1564 item.span,
1565 UnusedImportBracesDiag { node: node_name },
1566 );
1567 }
1568 }
1569}
1570
1571impl EarlyLintPass for UnusedImportBraces {
1572 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1573 if let ast::ItemKind::Use(ref use_tree) = item.kind {
1574 self.check_use_tree(cx, use_tree, item);
1575 }
1576 }
1577}
1578
1579declare_lint! {
1580 pub(super) UNUSED_ALLOCATION,
1599 Warn,
1600 "detects unnecessary allocations that can be eliminated"
1601}
1602
1603declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]);
1604
1605impl<'tcx> LateLintPass<'tcx> for UnusedAllocation {
1606 fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
1607 match e.kind {
1608 hir::ExprKind::Call(path_expr, [_])
1609 if let hir::ExprKind::Path(qpath) = &path_expr.kind
1610 && let Some(did) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()
1611 && cx.tcx.is_diagnostic_item(sym::box_new, did) => {}
1612 _ => return,
1613 }
1614
1615 for adj in cx.typeck_results().expr_adjustments(e) {
1616 if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(m)) = adj.kind {
1617 match m {
1618 adjustment::AutoBorrowMutability::Not => {
1619 cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag);
1620 }
1621 adjustment::AutoBorrowMutability::Mut { .. } => {
1622 cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag);
1623 }
1624 };
1625 }
1626 }
1627 }
1628}