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::Or(..) if avoid_or => return,
1195 PatKind::Ident(BindingMode::MUT, ..) if avoid_mut => {
1197 return;
1198 }
1199 _ => {}
1201 }
1202 let spans = if !value.span.from_expansion() {
1203 inner
1204 .span
1205 .find_ancestor_inside(value.span)
1206 .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi())))
1207 } else {
1208 None
1209 };
1210 self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false);
1211 }
1212 }
1213
1214 fn cast_followed_by_lt(&self, expr: &ast::Expr) -> Option<ast::NodeId> {
1215 if let ExprKind::Binary(op, lhs, _rhs) = &expr.kind
1216 && (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl)
1217 {
1218 let mut cur = lhs;
1219 while let ExprKind::Binary(_, _, rhs) = &cur.kind {
1220 cur = rhs;
1221 }
1222
1223 if let ExprKind::Cast(_, ty) = &cur.kind
1224 && let ast::TyKind::Paren(_) = &ty.kind
1225 {
1226 return Some(ty.id);
1227 }
1228 }
1229 None
1230 }
1231}
1232
1233impl EarlyLintPass for UnusedParens {
1234 #[inline]
1235 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1236 if let Some(ty_id) = self.cast_followed_by_lt(e) {
1237 self.parens_in_cast_in_lt.push(ty_id);
1238 }
1239
1240 match e.kind {
1241 ExprKind::Let(ref pat, _, _, _) | ExprKind::ForLoop { ref pat, .. } => {
1242 self.check_unused_parens_pat(cx, pat, false, false, (true, true));
1243 }
1244 ExprKind::If(ref cond, ref block, ref else_)
1248 if matches!(cond.peel_parens().kind, ExprKind::Let(..)) =>
1249 {
1250 self.check_unused_delims_expr(
1251 cx,
1252 cond.peel_parens(),
1253 UnusedDelimsCtx::LetScrutineeExpr,
1254 true,
1255 None,
1256 None,
1257 true,
1258 );
1259 for stmt in &block.stmts {
1260 <Self as UnusedDelimLint>::check_stmt(self, cx, stmt);
1261 }
1262 if let Some(e) = else_ {
1263 <Self as UnusedDelimLint>::check_expr(self, cx, e);
1264 }
1265 return;
1266 }
1267 ExprKind::Match(ref _expr, ref arm, _) => {
1268 for a in arm {
1269 if let Some(body) = &a.body {
1270 self.check_unused_delims_expr(
1271 cx,
1272 body,
1273 UnusedDelimsCtx::MatchArmExpr,
1274 false,
1275 None,
1276 None,
1277 true,
1278 );
1279 }
1280 }
1281 }
1282 _ => {}
1283 }
1284
1285 <Self as UnusedDelimLint>::check_expr(self, cx, e)
1286 }
1287
1288 fn check_expr_post(&mut self, _cx: &EarlyContext<'_>, e: &ast::Expr) {
1289 if let Some(ty_id) = self.cast_followed_by_lt(e) {
1290 let id = self
1291 .parens_in_cast_in_lt
1292 .pop()
1293 .expect("check_expr and check_expr_post must balance");
1294 assert_eq!(
1295 id, ty_id,
1296 "check_expr, check_ty, and check_expr_post are called, in that order, by the visitor"
1297 );
1298 }
1299 }
1300
1301 fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
1302 use ast::Mutability;
1303 use ast::PatKind::*;
1304 let keep_space = (false, false);
1305 match &p.kind {
1306 Paren(_)
1308 | Missing | Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None)
1310 | Path(..) | Err(_) => {},
1311 TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
1313 self.check_unused_parens_pat(cx, p, false, false, keep_space);
1314 },
1315 Struct(_, _, fps, _) => for f in fps {
1316 self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space);
1317 },
1318 Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
1320 Ref(p, _, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
1324 }
1325 }
1326
1327 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1328 if let StmtKind::Let(ref local) = s.kind {
1329 self.check_unused_parens_pat(cx, &local.pat, true, false, (true, false));
1330 }
1331
1332 <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1333 }
1334
1335 fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) {
1336 self.check_unused_parens_pat(cx, ¶m.pat, true, false, (false, false));
1337 }
1338
1339 fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
1340 self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false));
1341 }
1342
1343 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1344 if let ast::TyKind::Paren(_) = ty.kind
1345 && Some(&ty.id) == self.parens_in_cast_in_lt.last()
1346 {
1347 return;
1348 }
1349 match &ty.kind {
1350 ast::TyKind::Array(_, len) => {
1351 self.check_unused_delims_expr(
1352 cx,
1353 &len.value,
1354 UnusedDelimsCtx::ArrayLenExpr,
1355 false,
1356 None,
1357 None,
1358 false,
1359 );
1360 }
1361 ast::TyKind::Paren(r) => {
1362 let unused_parens = match &r.kind {
1363 ast::TyKind::ImplTrait(_, bounds) | ast::TyKind::TraitObject(bounds, _) => {
1364 match self.in_no_bounds_pos.get(&ty.id) {
1365 Some(NoBoundsException::None) => false,
1366 Some(NoBoundsException::OneBound) => bounds.len() <= 1,
1367 None => true,
1368 }
1369 }
1370 ast::TyKind::FnPtr(b) => {
1371 !self.with_self_ty_parens || b.generic_params.is_empty()
1372 }
1373 _ => true,
1374 };
1375
1376 if unused_parens {
1377 let spans = (!ty.span.from_expansion())
1378 .then(|| {
1379 r.span
1380 .find_ancestor_inside(ty.span)
1381 .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
1382 })
1383 .flatten();
1384
1385 self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false);
1386 }
1387
1388 self.with_self_ty_parens = false;
1389 }
1390 ast::TyKind::Ref(_, mut_ty) | ast::TyKind::Ptr(mut_ty) => {
1391 let own_constraint = self.in_no_bounds_pos.get(&ty.id);
1394 let constraint = match own_constraint {
1395 Some(NoBoundsException::None) => NoBoundsException::None,
1396 Some(NoBoundsException::OneBound) => NoBoundsException::OneBound,
1397 None => NoBoundsException::OneBound,
1398 };
1399 self.in_no_bounds_pos.insert(mut_ty.ty.id, constraint);
1400 }
1401 ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
1402 for i in 0..bounds.len() {
1403 let is_last = i == bounds.len() - 1;
1404
1405 if let ast::GenericBound::Trait(poly_trait_ref) = &bounds[i] {
1406 let fn_with_explicit_ret_ty = if let [.., segment] =
1407 &*poly_trait_ref.trait_ref.path.segments
1408 && let Some(args) = segment.args.as_ref()
1409 && let ast::GenericArgs::Parenthesized(paren_args) = &**args
1410 && let ast::FnRetTy::Ty(ret_ty) = &paren_args.output
1411 {
1412 self.in_no_bounds_pos.insert(
1413 ret_ty.id,
1414 if is_last {
1415 NoBoundsException::OneBound
1416 } else {
1417 NoBoundsException::None
1418 },
1419 );
1420
1421 true
1422 } else {
1423 false
1424 };
1425
1426 let dyn2015_exception = cx.sess().psess.edition == Edition2015
1431 && matches!(ty.kind, ast::TyKind::TraitObject(..))
1432 && i == 0
1433 && poly_trait_ref
1434 .trait_ref
1435 .path
1436 .segments
1437 .first()
1438 .map(|s| s.ident.name == kw::PathRoot)
1439 .unwrap_or(false);
1440
1441 if let ast::Parens::Yes = poly_trait_ref.parens
1442 && (is_last || !fn_with_explicit_ret_ty)
1443 && !dyn2015_exception
1444 {
1445 let s = poly_trait_ref.span;
1446 let spans = (!s.from_expansion()).then(|| {
1447 (
1448 s.with_hi(s.lo() + rustc_span::BytePos(1)),
1449 s.with_lo(s.hi() - rustc_span::BytePos(1)),
1450 )
1451 });
1452
1453 self.emit_unused_delims(
1454 cx,
1455 poly_trait_ref.span,
1456 spans,
1457 "type",
1458 (false, false),
1459 false,
1460 );
1461 }
1462 }
1463 }
1464 }
1465 _ => {}
1466 }
1467 }
1468
1469 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1470 <Self as UnusedDelimLint>::check_item(self, cx, item)
1471 }
1472
1473 fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Item) {
1474 self.in_no_bounds_pos.clear();
1475 }
1476
1477 fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) {
1478 use rustc_ast::{WhereBoundPredicate, WherePredicateKind};
1479 if let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
1480 bounded_ty,
1481 bound_generic_params,
1482 ..
1483 }) = &pred.kind
1484 && let ast::TyKind::Paren(_) = &bounded_ty.kind
1485 && bound_generic_params.is_empty()
1486 {
1487 self.with_self_ty_parens = true;
1488 }
1489 }
1490
1491 fn exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate) {
1492 assert!(!self.with_self_ty_parens);
1493 }
1494}
1495
1496declare_lint! {
1497 pub(super) UNUSED_BRACES,
1515 Warn,
1516 "unnecessary braces around an expression"
1517}
1518
1519declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]);
1520
1521impl UnusedDelimLint for UnusedBraces {
1522 const DELIM_STR: &'static str = "braces";
1523
1524 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = false;
1525
1526 fn lint(&self) -> &'static Lint {
1527 UNUSED_BRACES
1528 }
1529
1530 fn check_unused_delims_expr(
1531 &self,
1532 cx: &EarlyContext<'_>,
1533 value: &ast::Expr,
1534 ctx: UnusedDelimsCtx,
1535 followed_by_block: bool,
1536 left_pos: Option<BytePos>,
1537 right_pos: Option<BytePos>,
1538 is_kw: bool,
1539 ) {
1540 match value.kind {
1541 ast::ExprKind::Block(ref inner, None)
1542 if inner.rules == ast::BlockCheckMode::Default =>
1543 {
1544 if let [stmt] = inner.stmts.as_slice()
1569 && let ast::StmtKind::Expr(ref expr) = stmt.kind
1570 && !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
1571 && (ctx != UnusedDelimsCtx::AnonConst
1572 || (matches!(expr.kind, ast::ExprKind::Lit(_))
1573 && !expr.span.from_expansion()))
1574 && ctx != UnusedDelimsCtx::ClosureBody
1575 && !cx.sess().source_map().is_multiline(value.span)
1576 && value.attrs.is_empty()
1577 && !value.span.from_expansion()
1578 && !inner.span.from_expansion()
1579 {
1580 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1581 }
1582 }
1583 ast::ExprKind::Let(_, ref expr, _, _) => {
1584 self.check_unused_delims_expr(
1585 cx,
1586 expr,
1587 UnusedDelimsCtx::LetScrutineeExpr,
1588 followed_by_block,
1589 None,
1590 None,
1591 false,
1592 );
1593 }
1594 _ => {}
1595 }
1596 }
1597}
1598
1599impl EarlyLintPass for UnusedBraces {
1600 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1601 <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1602 }
1603
1604 #[inline]
1605 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1606 <Self as UnusedDelimLint>::check_expr(self, cx, e);
1607
1608 if let ExprKind::Repeat(_, ref anon_const) = e.kind {
1609 self.check_unused_delims_expr(
1610 cx,
1611 &anon_const.value,
1612 UnusedDelimsCtx::AnonConst,
1613 false,
1614 None,
1615 None,
1616 false,
1617 );
1618 }
1619 }
1620
1621 fn check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg) {
1622 if let ast::GenericArg::Const(ct) = arg {
1623 self.check_unused_delims_expr(
1624 cx,
1625 &ct.value,
1626 UnusedDelimsCtx::AnonConst,
1627 false,
1628 None,
1629 None,
1630 false,
1631 );
1632 }
1633 }
1634
1635 fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &ast::Variant) {
1636 if let Some(anon_const) = &v.disr_expr {
1637 self.check_unused_delims_expr(
1638 cx,
1639 &anon_const.value,
1640 UnusedDelimsCtx::AnonConst,
1641 false,
1642 None,
1643 None,
1644 false,
1645 );
1646 }
1647 }
1648
1649 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1650 match ty.kind {
1651 ast::TyKind::Array(_, ref len) => {
1652 self.check_unused_delims_expr(
1653 cx,
1654 &len.value,
1655 UnusedDelimsCtx::ArrayLenExpr,
1656 false,
1657 None,
1658 None,
1659 false,
1660 );
1661 }
1662
1663 ast::TyKind::Typeof(ref anon_const) => {
1664 self.check_unused_delims_expr(
1665 cx,
1666 &anon_const.value,
1667 UnusedDelimsCtx::AnonConst,
1668 false,
1669 None,
1670 None,
1671 false,
1672 );
1673 }
1674
1675 _ => {}
1676 }
1677 }
1678
1679 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1680 <Self as UnusedDelimLint>::check_item(self, cx, item)
1681 }
1682}
1683
1684declare_lint! {
1685 UNUSED_IMPORT_BRACES,
1710 Allow,
1711 "unnecessary braces around an imported item"
1712}
1713
1714declare_lint_pass!(UnusedImportBraces => [UNUSED_IMPORT_BRACES]);
1715
1716impl UnusedImportBraces {
1717 fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) {
1718 if let ast::UseTreeKind::Nested { ref items, .. } = use_tree.kind {
1719 for (tree, _) in items {
1721 self.check_use_tree(cx, tree, item);
1722 }
1723
1724 let [(tree, _)] = items.as_slice() else { return };
1726
1727 let node_name = match tree.kind {
1729 ast::UseTreeKind::Simple(rename) => {
1730 let orig_ident = tree.prefix.segments.last().unwrap().ident;
1731 if orig_ident.name == kw::SelfLower {
1732 return;
1733 }
1734 rename.unwrap_or(orig_ident).name
1735 }
1736 ast::UseTreeKind::Glob => sym::asterisk,
1737 ast::UseTreeKind::Nested { .. } => return,
1738 };
1739
1740 cx.emit_span_lint(
1741 UNUSED_IMPORT_BRACES,
1742 item.span,
1743 UnusedImportBracesDiag { node: node_name },
1744 );
1745 }
1746 }
1747}
1748
1749impl EarlyLintPass for UnusedImportBraces {
1750 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1751 if let ast::ItemKind::Use(ref use_tree) = item.kind {
1752 self.check_use_tree(cx, use_tree, item);
1753 }
1754 }
1755}
1756
1757declare_lint! {
1758 pub(super) UNUSED_ALLOCATION,
1777 Warn,
1778 "detects unnecessary allocations that can be eliminated"
1779}
1780
1781declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]);
1782
1783impl<'tcx> LateLintPass<'tcx> for UnusedAllocation {
1784 fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
1785 match e.kind {
1786 hir::ExprKind::Call(path_expr, [_])
1787 if let hir::ExprKind::Path(qpath) = &path_expr.kind
1788 && let Some(did) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()
1789 && cx.tcx.is_diagnostic_item(sym::box_new, did) => {}
1790 _ => return,
1791 }
1792
1793 for adj in cx.typeck_results().expr_adjustments(e) {
1794 if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(m)) = adj.kind {
1795 match m {
1796 adjustment::AutoBorrowMutability::Not => {
1797 cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag);
1798 }
1799 adjustment::AutoBorrowMutability::Mut { .. } => {
1800 cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag);
1801 }
1802 };
1803 }
1804 }
1805 }
1806}