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