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