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 hir::ExprKind::ConstBlock(block) => {
184 let body = cx.tcx.hir_body(block.body);
185 if let hir::ExprKind::Block(block, _) = body.value.kind
186 && let Some(expr) = block.expr
187 && let hir::ExprKind::OffsetOf(..) = expr.kind
188 {
189 Some("`offset_of` call")
190 } else {
191 None
192 }
193 }
194 _ => None,
195 };
196
197 let mut op_warned = false;
198
199 if let Some(must_use_op) = must_use_op {
200 let span = expr.span.find_ancestor_not_from_macro().unwrap_or(expr.span);
201 cx.emit_span_lint(
202 UNUSED_MUST_USE,
203 expr.span,
204 UnusedOp {
205 op: must_use_op,
206 label: expr.span,
207 suggestion: if expr_is_from_block {
208 UnusedOpSuggestion::BlockTailExpr {
209 before_span: span.shrink_to_lo(),
210 after_span: span.shrink_to_hi(),
211 }
212 } else {
213 UnusedOpSuggestion::NormalExpr { span: span.shrink_to_lo() }
214 },
215 },
216 );
217 op_warned = true;
218 }
219
220 if !(type_lint_emitted_or_suppressed || fn_warned || op_warned) {
221 cx.emit_span_lint(UNUSED_RESULTS, s.span, UnusedResult { ty });
222 }
223
224 fn check_fn_must_use(
225 cx: &LateContext<'_>,
226 expr: &hir::Expr<'_>,
227 expr_is_from_block: bool,
228 ) -> bool {
229 let maybe_def_id = match expr.kind {
230 hir::ExprKind::Call(callee, _) => {
231 match callee.kind {
232 hir::ExprKind::Path(ref qpath) => {
233 match cx.qpath_res(qpath, callee.hir_id) {
234 Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => Some(def_id),
235 _ => None,
238 }
239 }
240 _ => None,
241 }
242 }
243 hir::ExprKind::MethodCall(..) => {
244 cx.typeck_results().type_dependent_def_id(expr.hir_id)
245 }
246 _ => None,
247 };
248 if let Some(def_id) = maybe_def_id {
249 check_must_use_def(
250 cx,
251 def_id,
252 expr.span,
253 "return value of ",
254 "",
255 expr_is_from_block,
256 )
257 } else {
258 false
259 }
260 }
261
262 #[derive(Debug)]
264 enum MustUsePath {
265 Suppressed,
267 Def(Span, DefId, Option<Symbol>),
269 Boxed(Box<Self>),
270 Pinned(Box<Self>),
271 Opaque(Box<Self>),
272 TraitObject(Box<Self>),
273 TupleElement(Vec<(usize, Self)>),
274 Array(Box<Self>, u64),
275 Closure(Span),
277 Coroutine(Span),
279 }
280
281 #[instrument(skip(cx, expr), level = "debug", ret)]
282 fn is_ty_must_use<'tcx>(
283 cx: &LateContext<'tcx>,
284 ty: Ty<'tcx>,
285 expr: &hir::Expr<'_>,
286 span: Span,
287 ) -> Option<MustUsePath> {
288 if ty.is_unit() {
289 return Some(MustUsePath::Suppressed);
290 }
291 let parent_mod_did = cx.tcx.parent_module(expr.hir_id).to_def_id();
292 let is_uninhabited =
293 |t: Ty<'tcx>| !t.is_inhabited_from(cx.tcx, parent_mod_did, cx.typing_env());
294 if is_uninhabited(ty) {
295 return Some(MustUsePath::Suppressed);
296 }
297
298 match *ty.kind() {
299 ty::Adt(..) if let Some(boxed) = ty.boxed_ty() => {
300 is_ty_must_use(cx, boxed, expr, span)
301 .map(|inner| MustUsePath::Boxed(Box::new(inner)))
302 }
303 ty::Adt(def, args) if cx.tcx.is_lang_item(def.did(), LangItem::Pin) => {
304 let pinned_ty = args.type_at(0);
305 is_ty_must_use(cx, pinned_ty, expr, span)
306 .map(|inner| MustUsePath::Pinned(Box::new(inner)))
307 }
308 ty::Adt(def, args)
310 if cx.tcx.is_diagnostic_item(sym::Result, def.did())
311 && args.type_at(0).is_unit()
312 && is_uninhabited(args.type_at(1)) =>
313 {
314 Some(MustUsePath::Suppressed)
315 }
316 ty::Adt(def, args)
318 if cx.tcx.is_diagnostic_item(sym::ControlFlow, def.did())
319 && args.type_at(1).is_unit()
320 && is_uninhabited(args.type_at(0)) =>
321 {
322 Some(MustUsePath::Suppressed)
323 }
324 ty::Adt(def, _) => is_def_must_use(cx, def.did(), span),
325 ty::Alias(ty::Opaque | ty::Projection, ty::AliasTy { def_id: def, .. }) => {
326 elaborate(cx.tcx, cx.tcx.explicit_item_self_bounds(def).iter_identity_copied())
327 .filter_only_self()
329 .find_map(|(pred, _span)| {
330 if let ty::ClauseKind::Trait(ref poly_trait_predicate) =
332 pred.kind().skip_binder()
333 {
334 let def_id = poly_trait_predicate.trait_ref.def_id;
335
336 is_def_must_use(cx, def_id, span)
337 } else {
338 None
339 }
340 })
341 .map(|inner| MustUsePath::Opaque(Box::new(inner)))
342 }
343 ty::Dynamic(binders, _) => binders.iter().find_map(|predicate| {
344 if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
345 {
346 let def_id = trait_ref.def_id;
347 is_def_must_use(cx, def_id, span)
348 .map(|inner| MustUsePath::TraitObject(Box::new(inner)))
349 } else {
350 None
351 }
352 }),
353 ty::Tuple(tys) => {
354 let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind {
355 debug_assert_eq!(elem_exprs.len(), tys.len());
356 elem_exprs
357 } else {
358 &[]
359 };
360
361 let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr));
363
364 let nested_must_use = tys
365 .iter()
366 .zip(elem_exprs)
367 .enumerate()
368 .filter_map(|(i, (ty, expr))| {
369 is_ty_must_use(cx, ty, expr, expr.span).map(|path| (i, path))
370 })
371 .collect::<Vec<_>>();
372
373 if !nested_must_use.is_empty() {
374 Some(MustUsePath::TupleElement(nested_must_use))
375 } else {
376 None
377 }
378 }
379 ty::Array(ty, len) => match len.try_to_target_usize(cx.tcx) {
380 Some(0) | None => None,
382 Some(len) => is_ty_must_use(cx, ty, expr, span)
384 .map(|inner| MustUsePath::Array(Box::new(inner), len)),
385 },
386 ty::Closure(..) | ty::CoroutineClosure(..) => Some(MustUsePath::Closure(span)),
387 ty::Coroutine(def_id, ..) => {
388 let must_use = if cx.tcx.coroutine_is_async(def_id) {
390 let def_id = cx.tcx.lang_items().future_trait()?;
391 is_def_must_use(cx, def_id, span)
392 .map(|inner| MustUsePath::Opaque(Box::new(inner)))
393 } else {
394 None
395 };
396 must_use.or(Some(MustUsePath::Coroutine(span)))
397 }
398 _ => None,
399 }
400 }
401
402 fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option<MustUsePath> {
403 if let Some(reason) = find_attr!(
404 cx.tcx.get_all_attrs(def_id),
405 AttributeKind::MustUse { reason, .. } => reason
406 ) {
407 Some(MustUsePath::Def(span, def_id, *reason))
409 } else {
410 None
411 }
412 }
413
414 fn check_must_use_def(
417 cx: &LateContext<'_>,
418 def_id: DefId,
419 span: Span,
420 descr_pre_path: &str,
421 descr_post_path: &str,
422 expr_is_from_block: bool,
423 ) -> bool {
424 is_def_must_use(cx, def_id, span)
425 .map(|must_use_path| {
426 emit_must_use_untranslated(
427 cx,
428 &must_use_path,
429 descr_pre_path,
430 descr_post_path,
431 1,
432 false,
433 expr_is_from_block,
434 )
435 })
436 .is_some()
437 }
438
439 #[instrument(skip(cx), level = "debug")]
440 fn emit_must_use_untranslated(
441 cx: &LateContext<'_>,
442 path: &MustUsePath,
443 descr_pre: &str,
444 descr_post: &str,
445 plural_len: usize,
446 is_inner: bool,
447 expr_is_from_block: bool,
448 ) {
449 let plural_suffix = pluralize!(plural_len);
450
451 match path {
452 MustUsePath::Suppressed => {}
453 MustUsePath::Boxed(path) => {
454 let descr_pre = &format!("{descr_pre}boxed ");
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::Pinned(path) => {
466 let descr_pre = &format!("{descr_pre}pinned ");
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::Opaque(path) => {
478 let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of ");
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::TraitObject(path) => {
490 let descr_post = &format!(" trait object{plural_suffix}{descr_post}");
491 emit_must_use_untranslated(
492 cx,
493 path,
494 descr_pre,
495 descr_post,
496 plural_len,
497 true,
498 expr_is_from_block,
499 );
500 }
501 MustUsePath::TupleElement(elems) => {
502 for (index, path) in elems {
503 let descr_post = &format!(" in tuple element {index}");
504 emit_must_use_untranslated(
505 cx,
506 path,
507 descr_pre,
508 descr_post,
509 plural_len,
510 true,
511 expr_is_from_block,
512 );
513 }
514 }
515 MustUsePath::Array(path, len) => {
516 let descr_pre = &format!("{descr_pre}array{plural_suffix} of ");
517 emit_must_use_untranslated(
518 cx,
519 path,
520 descr_pre,
521 descr_post,
522 plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)),
523 true,
524 expr_is_from_block,
525 );
526 }
527 MustUsePath::Closure(span) => {
528 cx.emit_span_lint(
529 UNUSED_MUST_USE,
530 *span,
531 UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post },
532 );
533 }
534 MustUsePath::Coroutine(span) => {
535 cx.emit_span_lint(
536 UNUSED_MUST_USE,
537 *span,
538 UnusedCoroutine { count: plural_len, pre: descr_pre, post: descr_post },
539 );
540 }
541 MustUsePath::Def(span, def_id, reason) => {
542 let span = span.find_ancestor_not_from_macro().unwrap_or(*span);
543 cx.emit_span_lint(
544 UNUSED_MUST_USE,
545 span,
546 UnusedDef {
547 pre: descr_pre,
548 post: descr_post,
549 cx,
550 def_id: *def_id,
551 note: *reason,
552 suggestion: (!is_inner).then_some(if expr_is_from_block {
553 UnusedDefSuggestion::BlockTailExpr {
554 before_span: span.shrink_to_lo(),
555 after_span: span.shrink_to_hi(),
556 }
557 } else {
558 UnusedDefSuggestion::NormalExpr { span: span.shrink_to_lo() }
559 }),
560 },
561 );
562 }
563 }
564 }
565 }
566}
567
568declare_lint! {
569 pub PATH_STATEMENTS,
585 Warn,
586 "path statements with no effect"
587}
588
589declare_lint_pass!(PathStatements => [PATH_STATEMENTS]);
590
591impl<'tcx> LateLintPass<'tcx> for PathStatements {
592 fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) {
593 if let hir::StmtKind::Semi(expr) = s.kind
594 && let hir::ExprKind::Path(_) = expr.kind
595 {
596 let ty = cx.typeck_results().expr_ty(expr);
597 if ty.needs_drop(cx.tcx, cx.typing_env()) {
598 let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) {
599 PathStatementDropSub::Suggestion { span: s.span, snippet }
600 } else {
601 PathStatementDropSub::Help { span: s.span }
602 };
603 cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub })
604 } else {
605 cx.emit_span_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect);
606 }
607 }
608 }
609}
610
611#[derive(Copy, Clone, Debug, PartialEq, Eq)]
612enum UnusedDelimsCtx {
613 FunctionArg,
614 MethodArg,
615 AssignedValue,
616 AssignedValueLetElse,
617 IfCond,
618 WhileCond,
619 ForIterExpr,
620 MatchScrutineeExpr,
621 ReturnValue,
622 BlockRetValue,
623 BreakValue,
624 LetScrutineeExpr,
625 ArrayLenExpr,
626 AnonConst,
627 MatchArmExpr,
628 IndexExpr,
629 ClosureBody,
630}
631
632impl From<UnusedDelimsCtx> for &'static str {
633 fn from(ctx: UnusedDelimsCtx) -> &'static str {
634 match ctx {
635 UnusedDelimsCtx::FunctionArg => "function argument",
636 UnusedDelimsCtx::MethodArg => "method argument",
637 UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => {
638 "assigned value"
639 }
640 UnusedDelimsCtx::IfCond => "`if` condition",
641 UnusedDelimsCtx::WhileCond => "`while` condition",
642 UnusedDelimsCtx::ForIterExpr => "`for` iterator expression",
643 UnusedDelimsCtx::MatchScrutineeExpr => "`match` scrutinee expression",
644 UnusedDelimsCtx::ReturnValue => "`return` value",
645 UnusedDelimsCtx::BlockRetValue => "block return value",
646 UnusedDelimsCtx::BreakValue => "`break` value",
647 UnusedDelimsCtx::LetScrutineeExpr => "`let` scrutinee expression",
648 UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression",
649 UnusedDelimsCtx::MatchArmExpr => "match arm expression",
650 UnusedDelimsCtx::IndexExpr => "index expression",
651 UnusedDelimsCtx::ClosureBody => "closure body",
652 }
653 }
654}
655
656trait UnusedDelimLint {
658 const DELIM_STR: &'static str;
659
660 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool;
672
673 fn lint(&self) -> &'static Lint;
675
676 fn check_unused_delims_expr(
677 &self,
678 cx: &EarlyContext<'_>,
679 value: &ast::Expr,
680 ctx: UnusedDelimsCtx,
681 followed_by_block: bool,
682 left_pos: Option<BytePos>,
683 right_pos: Option<BytePos>,
684 is_kw: bool,
685 );
686
687 fn is_expr_delims_necessary(
688 inner: &ast::Expr,
689 ctx: UnusedDelimsCtx,
690 followed_by_block: bool,
691 ) -> bool {
692 let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
693
694 if followed_by_else {
695 match inner.kind {
696 ast::ExprKind::Binary(op, ..) if op.node.is_lazy() => return true,
697 _ if classify::expr_trailing_brace(inner).is_some() => return true,
698 _ => {}
699 }
700 }
701
702 if let ast::ExprKind::Range(..) = inner.kind
704 && matches!(ctx, UnusedDelimsCtx::LetScrutineeExpr)
705 {
706 return true;
707 }
708
709 if matches!(inner.kind, ast::ExprKind::AddrOf(ast::BorrowKind::Raw, ..)) {
713 return true;
714 }
715
716 {
746 let mut innermost = inner;
747 loop {
748 innermost = match &innermost.kind {
749 ExprKind::Binary(_op, lhs, _rhs) => lhs,
750 ExprKind::Call(fn_, _params) => fn_,
751 ExprKind::Cast(expr, _ty) => expr,
752 ExprKind::Type(expr, _ty) => expr,
753 ExprKind::Index(base, _subscript, _) => base,
754 _ => break,
755 };
756 if !classify::expr_requires_semi_to_be_stmt(innermost) {
757 return true;
758 }
759 }
760 }
761
762 if !followed_by_block {
765 return false;
766 }
767
768 {
770 let mut innermost = inner;
771 loop {
772 innermost = match &innermost.kind {
773 ExprKind::AddrOf(_, _, expr) => expr,
774 _ => {
775 if parser::contains_exterior_struct_lit(innermost) {
776 return true;
777 } else {
778 break;
779 }
780 }
781 }
782 }
783 }
784
785 let mut innermost = inner;
786 loop {
787 innermost = match &innermost.kind {
788 ExprKind::Unary(_op, expr) => expr,
789 ExprKind::Binary(_op, _lhs, rhs) => rhs,
790 ExprKind::AssignOp(_op, _lhs, rhs) => rhs,
791 ExprKind::Assign(_lhs, rhs, _span) => rhs,
792
793 ExprKind::Ret(_) | ExprKind::Yield(..) | ExprKind::Yeet(..) => return true,
794
795 ExprKind::Break(_label, None) => return false,
796 ExprKind::Break(_label, Some(break_expr)) => {
797 return matches!(break_expr.kind, ExprKind::Block(..));
798 }
799
800 ExprKind::Range(_lhs, Some(rhs), _limits) => {
801 return matches!(rhs.kind, ExprKind::Block(..));
802 }
803
804 _ => return parser::contains_exterior_struct_lit(inner),
805 }
806 }
807 }
808
809 fn emit_unused_delims_expr(
810 &self,
811 cx: &EarlyContext<'_>,
812 value: &ast::Expr,
813 ctx: UnusedDelimsCtx,
814 left_pos: Option<BytePos>,
815 right_pos: Option<BytePos>,
816 is_kw: bool,
817 ) {
818 let span_with_attrs = match value.kind {
819 ast::ExprKind::Block(ref block, None) if let [stmt] = block.stmts.as_slice() => {
820 if let Some(attr_lo) = stmt.attrs().iter().map(|attr| attr.span.lo()).min() {
823 stmt.span.with_lo(attr_lo)
824 } else {
825 stmt.span
826 }
827 }
828 ast::ExprKind::Paren(ref expr) => {
829 if let Some(attr_lo) = expr.attrs.iter().map(|attr| attr.span.lo()).min() {
832 expr.span.with_lo(attr_lo)
833 } else {
834 expr.span
835 }
836 }
837 _ => return,
838 };
839 let spans = span_with_attrs
840 .find_ancestor_inside(value.span)
841 .map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi())));
842 let keep_space = (
843 left_pos.is_some_and(|s| s >= value.span.lo()),
844 right_pos.is_some_and(|s| s <= value.span.hi()),
845 );
846 self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space, is_kw);
847 }
848
849 fn emit_unused_delims(
850 &self,
851 cx: &EarlyContext<'_>,
852 value_span: Span,
853 spans: Option<(Span, Span)>,
854 msg: &str,
855 keep_space: (bool, bool),
856 is_kw: bool,
857 ) {
858 let primary_span = if let Some((lo, hi)) = spans {
859 if hi.is_empty() {
860 return;
862 }
863 MultiSpan::from(vec![lo, hi])
864 } else {
865 MultiSpan::from(value_span)
866 };
867 let suggestion = spans.map(|(lo, hi)| {
868 let sm = cx.sess().source_map();
869 let lo_replace = if (keep_space.0 || is_kw)
870 && let Ok(snip) = sm.span_to_prev_source(lo)
871 && !snip.ends_with(' ')
872 {
873 " "
874 } else if let Ok(snip) = sm.span_to_prev_source(value_span)
875 && snip.ends_with(|c: char| c.is_alphanumeric())
876 {
877 " "
878 } else {
879 ""
880 };
881
882 let hi_replace = if keep_space.1
883 && let Ok(snip) = sm.span_to_next_source(hi)
884 && !snip.starts_with(' ')
885 {
886 " "
887 } else if let Ok(snip) = sm.span_to_prev_source(value_span)
888 && snip.starts_with(|c: char| c.is_alphanumeric())
889 {
890 " "
891 } else {
892 ""
893 };
894 UnusedDelimSuggestion {
895 start_span: lo,
896 start_replace: lo_replace,
897 end_span: hi,
898 end_replace: hi_replace,
899 }
900 });
901 cx.emit_span_lint(
902 self.lint(),
903 primary_span,
904 UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion },
905 );
906 }
907
908 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
909 use rustc_ast::ExprKind::*;
910 let (value, ctx, followed_by_block, left_pos, right_pos, is_kw) = match e.kind {
911 If(ref cond, ref block, _)
913 if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
914 {
915 let left = e.span.lo() + rustc_span::BytePos(2);
916 let right = block.span.lo();
917 (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right), true)
918 }
919
920 While(ref cond, ref block, ..)
922 if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
923 {
924 let left = e.span.lo() + rustc_span::BytePos(5);
925 let right = block.span.lo();
926 (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right), true)
927 }
928
929 ForLoop { ref iter, ref body, .. } => {
930 (iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true)
931 }
932
933 Match(ref head, _, ast::MatchKind::Prefix)
934 if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
935 {
936 let left = e.span.lo() + rustc_span::BytePos(5);
937 (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true)
938 }
939
940 Ret(Some(ref value)) => {
941 let left = e.span.lo() + rustc_span::BytePos(3);
942 (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true)
943 }
944
945 Break(label, Some(ref value)) => {
946 if label.is_some()
950 && matches!(value.kind, ast::ExprKind::Paren(ref inner)
951 if matches!(inner.kind, ast::ExprKind::Block(..)))
952 {
953 return;
954 }
955 (value, UnusedDelimsCtx::BreakValue, false, None, None, true)
956 }
957
958 Index(_, ref value, _) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false),
959
960 Assign(_, ref value, _) | AssignOp(.., ref value) => {
961 (value, UnusedDelimsCtx::AssignedValue, false, None, None, false)
962 }
963 ref call_or_other => {
965 let (args_to_check, ctx) = match *call_or_other {
966 Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg),
967 MethodCall(ref call) => (&call.args[..], UnusedDelimsCtx::MethodArg),
968 Closure(ref closure)
969 if matches!(closure.fn_decl.output, FnRetTy::Default(_)) =>
970 {
971 (&[closure.body.clone()][..], UnusedDelimsCtx::ClosureBody)
972 }
973 _ => {
975 return;
976 }
977 };
978 if e.span.ctxt().outer_expn_data().call_site.from_expansion() {
983 return;
984 }
985 for arg in args_to_check {
986 self.check_unused_delims_expr(cx, arg, ctx, false, None, None, false);
987 }
988 return;
989 }
990 };
991 self.check_unused_delims_expr(
992 cx,
993 value,
994 ctx,
995 followed_by_block,
996 left_pos,
997 right_pos,
998 is_kw,
999 );
1000 }
1001
1002 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1003 match s.kind {
1004 StmtKind::Let(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
1005 if let Some((init, els)) = local.kind.init_else_opt() {
1006 if els.is_some()
1007 && let ExprKind::Paren(paren) = &init.kind
1008 && !init.span.eq_ctxt(paren.span)
1009 {
1010 return;
1021 }
1022 let ctx = match els {
1023 None => UnusedDelimsCtx::AssignedValue,
1024 Some(_) => UnusedDelimsCtx::AssignedValueLetElse,
1025 };
1026 self.check_unused_delims_expr(cx, init, ctx, false, None, None, false);
1027 }
1028 }
1029 StmtKind::Expr(ref expr) => {
1030 self.check_unused_delims_expr(
1031 cx,
1032 expr,
1033 UnusedDelimsCtx::BlockRetValue,
1034 false,
1035 None,
1036 None,
1037 false,
1038 );
1039 }
1040 _ => {}
1041 }
1042 }
1043
1044 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1045 use ast::ItemKind::*;
1046
1047 let expr = if let Const(box ast::ConstItem { rhs: Some(rhs), .. }) = &item.kind {
1048 rhs.expr()
1049 } else if let Static(box ast::StaticItem { expr: Some(expr), .. }) = &item.kind {
1050 expr
1051 } else {
1052 return;
1053 };
1054 self.check_unused_delims_expr(
1055 cx,
1056 expr,
1057 UnusedDelimsCtx::AssignedValue,
1058 false,
1059 None,
1060 None,
1061 false,
1062 );
1063 }
1064}
1065
1066declare_lint! {
1067 pub(super) UNUSED_PARENS,
1083 Warn,
1084 "`if`, `match`, `while` and `return` do not need parentheses"
1085}
1086
1087#[derive(Default)]
1088pub(crate) struct UnusedParens {
1089 with_self_ty_parens: bool,
1090 parens_in_cast_in_lt: Vec<ast::NodeId>,
1093 in_no_bounds_pos: FxHashMap<ast::NodeId, NoBoundsException>,
1096}
1097
1098enum NoBoundsException {
1113 None,
1115 OneBound,
1118}
1119
1120impl_lint_pass!(UnusedParens => [UNUSED_PARENS]);
1121
1122impl UnusedDelimLint for UnusedParens {
1123 const DELIM_STR: &'static str = "parentheses";
1124
1125 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = true;
1126
1127 fn lint(&self) -> &'static Lint {
1128 UNUSED_PARENS
1129 }
1130
1131 fn check_unused_delims_expr(
1132 &self,
1133 cx: &EarlyContext<'_>,
1134 value: &ast::Expr,
1135 ctx: UnusedDelimsCtx,
1136 followed_by_block: bool,
1137 left_pos: Option<BytePos>,
1138 right_pos: Option<BytePos>,
1139 is_kw: bool,
1140 ) {
1141 match value.kind {
1142 ast::ExprKind::Paren(ref inner) => {
1143 if !Self::is_expr_delims_necessary(inner, ctx, followed_by_block)
1144 && value.attrs.is_empty()
1145 && !value.span.from_expansion()
1146 && (ctx != UnusedDelimsCtx::LetScrutineeExpr
1147 || !matches!(inner.kind, ast::ExprKind::Binary(
1148 rustc_span::source_map::Spanned { node, .. },
1149 _,
1150 _,
1151 ) if node.is_lazy()))
1152 && !((ctx == UnusedDelimsCtx::ReturnValue
1153 || ctx == UnusedDelimsCtx::BreakValue)
1154 && matches!(inner.kind, ast::ExprKind::Assign(_, _, _)))
1155 {
1156 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1157 }
1158 }
1159 ast::ExprKind::Let(_, ref expr, _, _) => {
1160 self.check_unused_delims_expr(
1161 cx,
1162 expr,
1163 UnusedDelimsCtx::LetScrutineeExpr,
1164 followed_by_block,
1165 None,
1166 None,
1167 false,
1168 );
1169 }
1170 _ => {}
1171 }
1172 }
1173}
1174
1175impl UnusedParens {
1176 fn check_unused_parens_pat(
1177 &self,
1178 cx: &EarlyContext<'_>,
1179 value: &ast::Pat,
1180 avoid_or: bool,
1181 avoid_mut: bool,
1182 keep_space: (bool, bool),
1183 ) {
1184 use ast::{BindingMode, PatKind};
1185
1186 if let PatKind::Paren(inner) = &value.kind {
1187 match inner.kind {
1188 PatKind::Range(..) => return,
1193 PatKind::Guard(..) => return,
1195 PatKind::Or(..) if avoid_or => return,
1197 PatKind::Ident(BindingMode::MUT, ..) if avoid_mut => {
1199 return;
1200 }
1201 _ => {}
1203 }
1204 let spans = if !value.span.from_expansion() {
1205 inner
1206 .span
1207 .find_ancestor_inside(value.span)
1208 .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi())))
1209 } else {
1210 None
1211 };
1212 self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false);
1213 }
1214 }
1215
1216 fn cast_followed_by_lt(&self, expr: &ast::Expr) -> Option<ast::NodeId> {
1217 if let ExprKind::Binary(op, lhs, _rhs) = &expr.kind
1218 && (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl)
1219 {
1220 let mut cur = lhs;
1221 while let ExprKind::Binary(_, _, rhs) = &cur.kind {
1222 cur = rhs;
1223 }
1224
1225 if let ExprKind::Cast(_, ty) = &cur.kind
1226 && let ast::TyKind::Paren(_) = &ty.kind
1227 {
1228 return Some(ty.id);
1229 }
1230 }
1231 None
1232 }
1233}
1234
1235impl EarlyLintPass for UnusedParens {
1236 #[inline]
1237 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1238 if let Some(ty_id) = self.cast_followed_by_lt(e) {
1239 self.parens_in_cast_in_lt.push(ty_id);
1240 }
1241
1242 match e.kind {
1243 ExprKind::Let(ref pat, _, _, _) | ExprKind::ForLoop { ref pat, .. } => {
1244 self.check_unused_parens_pat(cx, pat, false, false, (true, true));
1245 }
1246 ExprKind::If(ref cond, ref block, ref else_)
1250 if matches!(cond.peel_parens().kind, ExprKind::Let(..)) =>
1251 {
1252 self.check_unused_delims_expr(
1253 cx,
1254 cond.peel_parens(),
1255 UnusedDelimsCtx::LetScrutineeExpr,
1256 true,
1257 None,
1258 None,
1259 true,
1260 );
1261 for stmt in &block.stmts {
1262 <Self as UnusedDelimLint>::check_stmt(self, cx, stmt);
1263 }
1264 if let Some(e) = else_ {
1265 <Self as UnusedDelimLint>::check_expr(self, cx, e);
1266 }
1267 return;
1268 }
1269 ExprKind::Match(ref _expr, ref arm, _) => {
1270 for a in arm {
1271 if let Some(body) = &a.body {
1272 self.check_unused_delims_expr(
1273 cx,
1274 body,
1275 UnusedDelimsCtx::MatchArmExpr,
1276 false,
1277 None,
1278 None,
1279 true,
1280 );
1281 }
1282 }
1283 }
1284 _ => {}
1285 }
1286
1287 <Self as UnusedDelimLint>::check_expr(self, cx, e)
1288 }
1289
1290 fn check_expr_post(&mut self, _cx: &EarlyContext<'_>, e: &ast::Expr) {
1291 if let Some(ty_id) = self.cast_followed_by_lt(e) {
1292 let id = self
1293 .parens_in_cast_in_lt
1294 .pop()
1295 .expect("check_expr and check_expr_post must balance");
1296 assert_eq!(
1297 id, ty_id,
1298 "check_expr, check_ty, and check_expr_post are called, in that order, by the visitor"
1299 );
1300 }
1301 }
1302
1303 fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
1304 use ast::Mutability;
1305 use ast::PatKind::*;
1306 let keep_space = (false, false);
1307 match &p.kind {
1308 Paren(_)
1310 | Missing | Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None)
1312 | Path(..) | Err(_) => {},
1313 TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
1315 self.check_unused_parens_pat(cx, p, false, false, keep_space);
1316 },
1317 Struct(_, _, fps, _) => for f in fps {
1318 self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space);
1319 },
1320 Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
1322 Ref(p, _, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
1326 }
1327 }
1328
1329 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1330 if let StmtKind::Let(ref local) = s.kind {
1331 self.check_unused_parens_pat(cx, &local.pat, true, false, (true, false));
1332 }
1333
1334 <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1335 }
1336
1337 fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) {
1338 self.check_unused_parens_pat(cx, ¶m.pat, true, false, (false, false));
1339 }
1340
1341 fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
1342 self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false));
1343 }
1344
1345 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1346 if let ast::TyKind::Paren(_) = ty.kind
1347 && Some(&ty.id) == self.parens_in_cast_in_lt.last()
1348 {
1349 return;
1350 }
1351 match &ty.kind {
1352 ast::TyKind::Array(_, len) => {
1353 self.check_unused_delims_expr(
1354 cx,
1355 &len.value,
1356 UnusedDelimsCtx::ArrayLenExpr,
1357 false,
1358 None,
1359 None,
1360 false,
1361 );
1362 }
1363 ast::TyKind::Paren(r) => {
1364 let unused_parens = match &r.kind {
1365 ast::TyKind::ImplTrait(_, bounds) | ast::TyKind::TraitObject(bounds, _) => {
1366 match self.in_no_bounds_pos.get(&ty.id) {
1367 Some(NoBoundsException::None) => false,
1368 Some(NoBoundsException::OneBound) => bounds.len() <= 1,
1369 None => true,
1370 }
1371 }
1372 ast::TyKind::FnPtr(b) => {
1373 !self.with_self_ty_parens || b.generic_params.is_empty()
1374 }
1375 _ => true,
1376 };
1377
1378 if unused_parens {
1379 let spans = (!ty.span.from_expansion())
1380 .then(|| {
1381 r.span
1382 .find_ancestor_inside(ty.span)
1383 .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
1384 })
1385 .flatten();
1386
1387 self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false);
1388 }
1389
1390 self.with_self_ty_parens = false;
1391 }
1392 ast::TyKind::Ref(_, mut_ty) | ast::TyKind::Ptr(mut_ty) => {
1393 let own_constraint = self.in_no_bounds_pos.get(&ty.id);
1396 let constraint = match own_constraint {
1397 Some(NoBoundsException::None) => NoBoundsException::None,
1398 Some(NoBoundsException::OneBound) => NoBoundsException::OneBound,
1399 None => NoBoundsException::OneBound,
1400 };
1401 self.in_no_bounds_pos.insert(mut_ty.ty.id, constraint);
1402 }
1403 ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
1404 for i in 0..bounds.len() {
1405 let is_last = i == bounds.len() - 1;
1406
1407 if let ast::GenericBound::Trait(poly_trait_ref) = &bounds[i] {
1408 let fn_with_explicit_ret_ty = if let [.., segment] =
1409 &*poly_trait_ref.trait_ref.path.segments
1410 && let Some(args) = segment.args.as_ref()
1411 && let ast::GenericArgs::Parenthesized(paren_args) = &**args
1412 && let ast::FnRetTy::Ty(ret_ty) = &paren_args.output
1413 {
1414 self.in_no_bounds_pos.insert(
1415 ret_ty.id,
1416 if is_last {
1417 NoBoundsException::OneBound
1418 } else {
1419 NoBoundsException::None
1420 },
1421 );
1422
1423 true
1424 } else {
1425 false
1426 };
1427
1428 let dyn2015_exception = cx.sess().psess.edition == Edition2015
1433 && matches!(ty.kind, ast::TyKind::TraitObject(..))
1434 && i == 0
1435 && poly_trait_ref
1436 .trait_ref
1437 .path
1438 .segments
1439 .first()
1440 .map(|s| s.ident.name == kw::PathRoot)
1441 .unwrap_or(false);
1442
1443 if let ast::Parens::Yes = poly_trait_ref.parens
1444 && (is_last || !fn_with_explicit_ret_ty)
1445 && !dyn2015_exception
1446 {
1447 let s = poly_trait_ref.span;
1448 let spans = (!s.from_expansion()).then(|| {
1449 (
1450 s.with_hi(s.lo() + rustc_span::BytePos(1)),
1451 s.with_lo(s.hi() - rustc_span::BytePos(1)),
1452 )
1453 });
1454
1455 self.emit_unused_delims(
1456 cx,
1457 poly_trait_ref.span,
1458 spans,
1459 "type",
1460 (false, false),
1461 false,
1462 );
1463 }
1464 }
1465 }
1466 }
1467 _ => {}
1468 }
1469 }
1470
1471 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1472 <Self as UnusedDelimLint>::check_item(self, cx, item)
1473 }
1474
1475 fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Item) {
1476 self.in_no_bounds_pos.clear();
1477 }
1478
1479 fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) {
1480 use rustc_ast::{WhereBoundPredicate, WherePredicateKind};
1481 if let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
1482 bounded_ty,
1483 bound_generic_params,
1484 ..
1485 }) = &pred.kind
1486 && let ast::TyKind::Paren(_) = &bounded_ty.kind
1487 && bound_generic_params.is_empty()
1488 {
1489 self.with_self_ty_parens = true;
1490 }
1491 }
1492
1493 fn exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate) {
1494 assert!(!self.with_self_ty_parens);
1495 }
1496}
1497
1498declare_lint! {
1499 pub(super) UNUSED_BRACES,
1517 Warn,
1518 "unnecessary braces around an expression"
1519}
1520
1521declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]);
1522
1523impl UnusedDelimLint for UnusedBraces {
1524 const DELIM_STR: &'static str = "braces";
1525
1526 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = false;
1527
1528 fn lint(&self) -> &'static Lint {
1529 UNUSED_BRACES
1530 }
1531
1532 fn check_unused_delims_expr(
1533 &self,
1534 cx: &EarlyContext<'_>,
1535 value: &ast::Expr,
1536 ctx: UnusedDelimsCtx,
1537 followed_by_block: bool,
1538 left_pos: Option<BytePos>,
1539 right_pos: Option<BytePos>,
1540 is_kw: bool,
1541 ) {
1542 match value.kind {
1543 ast::ExprKind::Block(ref inner, None)
1544 if inner.rules == ast::BlockCheckMode::Default =>
1545 {
1546 if let [stmt] = inner.stmts.as_slice()
1571 && let ast::StmtKind::Expr(ref expr) = stmt.kind
1572 && !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
1573 && (ctx != UnusedDelimsCtx::AnonConst
1574 || (matches!(expr.kind, ast::ExprKind::Lit(_))
1575 && !expr.span.from_expansion()))
1576 && ctx != UnusedDelimsCtx::ClosureBody
1577 && !cx.sess().source_map().is_multiline(value.span)
1578 && value.attrs.is_empty()
1579 && !value.span.from_expansion()
1580 && !inner.span.from_expansion()
1581 {
1582 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1583 }
1584 }
1585 ast::ExprKind::Let(_, ref expr, _, _) => {
1586 self.check_unused_delims_expr(
1587 cx,
1588 expr,
1589 UnusedDelimsCtx::LetScrutineeExpr,
1590 followed_by_block,
1591 None,
1592 None,
1593 false,
1594 );
1595 }
1596 _ => {}
1597 }
1598 }
1599}
1600
1601impl EarlyLintPass for UnusedBraces {
1602 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1603 <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1604 }
1605
1606 #[inline]
1607 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1608 <Self as UnusedDelimLint>::check_expr(self, cx, e);
1609
1610 if let ExprKind::Repeat(_, ref anon_const) = e.kind {
1611 self.check_unused_delims_expr(
1612 cx,
1613 &anon_const.value,
1614 UnusedDelimsCtx::AnonConst,
1615 false,
1616 None,
1617 None,
1618 false,
1619 );
1620 }
1621 }
1622
1623 fn check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg) {
1624 if let ast::GenericArg::Const(ct) = arg {
1625 self.check_unused_delims_expr(
1626 cx,
1627 &ct.value,
1628 UnusedDelimsCtx::AnonConst,
1629 false,
1630 None,
1631 None,
1632 false,
1633 );
1634 }
1635 }
1636
1637 fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &ast::Variant) {
1638 if let Some(anon_const) = &v.disr_expr {
1639 self.check_unused_delims_expr(
1640 cx,
1641 &anon_const.value,
1642 UnusedDelimsCtx::AnonConst,
1643 false,
1644 None,
1645 None,
1646 false,
1647 );
1648 }
1649 }
1650
1651 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1652 match ty.kind {
1653 ast::TyKind::Array(_, ref len) => {
1654 self.check_unused_delims_expr(
1655 cx,
1656 &len.value,
1657 UnusedDelimsCtx::ArrayLenExpr,
1658 false,
1659 None,
1660 None,
1661 false,
1662 );
1663 }
1664
1665 _ => {}
1666 }
1667 }
1668
1669 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1670 <Self as UnusedDelimLint>::check_item(self, cx, item)
1671 }
1672}
1673
1674declare_lint! {
1675 UNUSED_IMPORT_BRACES,
1700 Allow,
1701 "unnecessary braces around an imported item"
1702}
1703
1704declare_lint_pass!(UnusedImportBraces => [UNUSED_IMPORT_BRACES]);
1705
1706impl UnusedImportBraces {
1707 fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) {
1708 if let ast::UseTreeKind::Nested { ref items, .. } = use_tree.kind {
1709 for (tree, _) in items {
1711 self.check_use_tree(cx, tree, item);
1712 }
1713
1714 let [(tree, _)] = items.as_slice() else { return };
1716
1717 let node_name = match tree.kind {
1719 ast::UseTreeKind::Simple(rename) => {
1720 let orig_ident = tree.prefix.segments.last().unwrap().ident;
1721 if orig_ident.name == kw::SelfLower {
1722 return;
1723 }
1724 rename.unwrap_or(orig_ident).name
1725 }
1726 ast::UseTreeKind::Glob => sym::asterisk,
1727 ast::UseTreeKind::Nested { .. } => return,
1728 };
1729
1730 cx.emit_span_lint(
1731 UNUSED_IMPORT_BRACES,
1732 item.span,
1733 UnusedImportBracesDiag { node: node_name },
1734 );
1735 }
1736 }
1737}
1738
1739impl EarlyLintPass for UnusedImportBraces {
1740 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1741 if let ast::ItemKind::Use(ref use_tree) = item.kind {
1742 self.check_use_tree(cx, use_tree, item);
1743 }
1744 }
1745}
1746
1747declare_lint! {
1748 pub(super) UNUSED_ALLOCATION,
1767 Warn,
1768 "detects unnecessary allocations that can be eliminated"
1769}
1770
1771declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]);
1772
1773impl<'tcx> LateLintPass<'tcx> for UnusedAllocation {
1774 fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
1775 match e.kind {
1776 hir::ExprKind::Call(path_expr, [_])
1777 if let hir::ExprKind::Path(qpath) = &path_expr.kind
1778 && let Some(did) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()
1779 && cx.tcx.is_diagnostic_item(sym::box_new, did) => {}
1780 _ => return,
1781 }
1782
1783 for adj in cx.typeck_results().expr_adjustments(e) {
1784 if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(m)) = adj.kind {
1785 match m {
1786 adjustment::AutoBorrowMutability::Not => {
1787 cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag);
1788 }
1789 adjustment::AutoBorrowMutability::Mut { .. } => {
1790 cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag);
1791 }
1792 };
1793 }
1794 }
1795 }
1796}