1use std::iter;
2
3use rustc_ast::util::{classify, parser};
4use rustc_ast::{self as ast, ExprKind, FnRetTy, HasAttrs as _, StmtKind};
5use rustc_attr_data_structures::{AttributeKind, find_attr};
6use rustc_data_structures::fx::FxHashMap;
7use rustc_errors::{MultiSpan, pluralize};
8use rustc_hir::def::{DefKind, Res};
9use rustc_hir::def_id::DefId;
10use rustc_hir::{self as hir, LangItem};
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_oldest_ancestor_in_same_ctxt();
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_oldest_ancestor_in_same_ctxt();
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 if let hir::ExprKind::Path(_) = expr.kind {
567 let ty = cx.typeck_results().expr_ty(expr);
568 if ty.needs_drop(cx.tcx, cx.typing_env()) {
569 let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span)
570 {
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
584#[derive(Copy, Clone, Debug, PartialEq, Eq)]
585enum UnusedDelimsCtx {
586 FunctionArg,
587 MethodArg,
588 AssignedValue,
589 AssignedValueLetElse,
590 IfCond,
591 WhileCond,
592 ForIterExpr,
593 MatchScrutineeExpr,
594 ReturnValue,
595 BlockRetValue,
596 BreakValue,
597 LetScrutineeExpr,
598 ArrayLenExpr,
599 AnonConst,
600 MatchArmExpr,
601 IndexExpr,
602 ClosureBody,
603}
604
605impl From<UnusedDelimsCtx> for &'static str {
606 fn from(ctx: UnusedDelimsCtx) -> &'static str {
607 match ctx {
608 UnusedDelimsCtx::FunctionArg => "function argument",
609 UnusedDelimsCtx::MethodArg => "method argument",
610 UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => {
611 "assigned value"
612 }
613 UnusedDelimsCtx::IfCond => "`if` condition",
614 UnusedDelimsCtx::WhileCond => "`while` condition",
615 UnusedDelimsCtx::ForIterExpr => "`for` iterator expression",
616 UnusedDelimsCtx::MatchScrutineeExpr => "`match` scrutinee expression",
617 UnusedDelimsCtx::ReturnValue => "`return` value",
618 UnusedDelimsCtx::BlockRetValue => "block return value",
619 UnusedDelimsCtx::BreakValue => "`break` value",
620 UnusedDelimsCtx::LetScrutineeExpr => "`let` scrutinee expression",
621 UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression",
622 UnusedDelimsCtx::MatchArmExpr => "match arm expression",
623 UnusedDelimsCtx::IndexExpr => "index expression",
624 UnusedDelimsCtx::ClosureBody => "closure body",
625 }
626 }
627}
628
629trait UnusedDelimLint {
631 const DELIM_STR: &'static str;
632
633 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool;
645
646 fn lint(&self) -> &'static Lint;
648
649 fn check_unused_delims_expr(
650 &self,
651 cx: &EarlyContext<'_>,
652 value: &ast::Expr,
653 ctx: UnusedDelimsCtx,
654 followed_by_block: bool,
655 left_pos: Option<BytePos>,
656 right_pos: Option<BytePos>,
657 is_kw: bool,
658 );
659
660 fn is_expr_delims_necessary(
661 inner: &ast::Expr,
662 ctx: UnusedDelimsCtx,
663 followed_by_block: bool,
664 ) -> bool {
665 let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse;
666
667 if followed_by_else {
668 match inner.kind {
669 ast::ExprKind::Binary(op, ..) if op.node.is_lazy() => return true,
670 _ if classify::expr_trailing_brace(inner).is_some() => return true,
671 _ => {}
672 }
673 }
674
675 if let ast::ExprKind::Range(..) = inner.kind
677 && matches!(ctx, UnusedDelimsCtx::LetScrutineeExpr)
678 {
679 return true;
680 }
681
682 if matches!(inner.kind, ast::ExprKind::AddrOf(ast::BorrowKind::Raw, ..)) {
686 return true;
687 }
688
689 {
719 let mut innermost = inner;
720 loop {
721 innermost = match &innermost.kind {
722 ExprKind::Binary(_op, lhs, _rhs) => lhs,
723 ExprKind::Call(fn_, _params) => fn_,
724 ExprKind::Cast(expr, _ty) => expr,
725 ExprKind::Type(expr, _ty) => expr,
726 ExprKind::Index(base, _subscript, _) => base,
727 _ => break,
728 };
729 if !classify::expr_requires_semi_to_be_stmt(innermost) {
730 return true;
731 }
732 }
733 }
734
735 if !followed_by_block {
738 return false;
739 }
740
741 {
743 let mut innermost = inner;
744 loop {
745 innermost = match &innermost.kind {
746 ExprKind::AddrOf(_, _, expr) => expr,
747 _ => {
748 if parser::contains_exterior_struct_lit(innermost) {
749 return true;
750 } else {
751 break;
752 }
753 }
754 }
755 }
756 }
757
758 let mut innermost = inner;
759 loop {
760 innermost = match &innermost.kind {
761 ExprKind::Unary(_op, expr) => expr,
762 ExprKind::Binary(_op, _lhs, rhs) => rhs,
763 ExprKind::AssignOp(_op, _lhs, rhs) => rhs,
764 ExprKind::Assign(_lhs, rhs, _span) => rhs,
765
766 ExprKind::Ret(_) | ExprKind::Yield(..) | ExprKind::Yeet(..) => return true,
767
768 ExprKind::Break(_label, None) => return false,
769 ExprKind::Break(_label, Some(break_expr)) => {
770 return matches!(break_expr.kind, ExprKind::Block(..));
771 }
772
773 ExprKind::Range(_lhs, Some(rhs), _limits) => {
774 return matches!(rhs.kind, ExprKind::Block(..));
775 }
776
777 _ => return parser::contains_exterior_struct_lit(inner),
778 }
779 }
780 }
781
782 fn emit_unused_delims_expr(
783 &self,
784 cx: &EarlyContext<'_>,
785 value: &ast::Expr,
786 ctx: UnusedDelimsCtx,
787 left_pos: Option<BytePos>,
788 right_pos: Option<BytePos>,
789 is_kw: bool,
790 ) {
791 let span_with_attrs = match value.kind {
792 ast::ExprKind::Block(ref block, None) if let [stmt] = block.stmts.as_slice() => {
793 if let Some(attr_lo) = stmt.attrs().iter().map(|attr| attr.span.lo()).min() {
796 stmt.span.with_lo(attr_lo)
797 } else {
798 stmt.span
799 }
800 }
801 ast::ExprKind::Paren(ref expr) => {
802 if let Some(attr_lo) = expr.attrs.iter().map(|attr| attr.span.lo()).min() {
805 expr.span.with_lo(attr_lo)
806 } else {
807 expr.span
808 }
809 }
810 _ => return,
811 };
812 let spans = span_with_attrs
813 .find_ancestor_inside(value.span)
814 .map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi())));
815 let keep_space = (
816 left_pos.is_some_and(|s| s >= value.span.lo()),
817 right_pos.is_some_and(|s| s <= value.span.hi()),
818 );
819 self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space, is_kw);
820 }
821
822 fn emit_unused_delims(
823 &self,
824 cx: &EarlyContext<'_>,
825 value_span: Span,
826 spans: Option<(Span, Span)>,
827 msg: &str,
828 keep_space: (bool, bool),
829 is_kw: bool,
830 ) {
831 let primary_span = if let Some((lo, hi)) = spans {
832 if hi.is_empty() {
833 return;
835 }
836 MultiSpan::from(vec![lo, hi])
837 } else {
838 MultiSpan::from(value_span)
839 };
840 let suggestion = spans.map(|(lo, hi)| {
841 let sm = cx.sess().source_map();
842 let lo_replace = if (keep_space.0 || is_kw)
843 && let Ok(snip) = sm.span_to_prev_source(lo)
844 && !snip.ends_with(' ')
845 {
846 " "
847 } else {
848 ""
849 };
850
851 let hi_replace = if keep_space.1
852 && let Ok(snip) = sm.span_to_next_source(hi)
853 && !snip.starts_with(' ')
854 {
855 " "
856 } else {
857 ""
858 };
859 UnusedDelimSuggestion {
860 start_span: lo,
861 start_replace: lo_replace,
862 end_span: hi,
863 end_replace: hi_replace,
864 }
865 });
866 cx.emit_span_lint(
867 self.lint(),
868 primary_span,
869 UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion },
870 );
871 }
872
873 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
874 use rustc_ast::ExprKind::*;
875 let (value, ctx, followed_by_block, left_pos, right_pos, is_kw) = match e.kind {
876 If(ref cond, ref block, _)
878 if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
879 {
880 let left = e.span.lo() + rustc_span::BytePos(2);
881 let right = block.span.lo();
882 (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right), true)
883 }
884
885 While(ref cond, ref block, ..)
887 if !matches!(cond.kind, Let(..)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
888 {
889 let left = e.span.lo() + rustc_span::BytePos(5);
890 let right = block.span.lo();
891 (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right), true)
892 }
893
894 ForLoop { ref iter, ref body, .. } => {
895 (iter, UnusedDelimsCtx::ForIterExpr, true, None, Some(body.span.lo()), true)
896 }
897
898 Match(ref head, _, ast::MatchKind::Prefix)
899 if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
900 {
901 let left = e.span.lo() + rustc_span::BytePos(5);
902 (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true)
903 }
904
905 Ret(Some(ref value)) => {
906 let left = e.span.lo() + rustc_span::BytePos(3);
907 (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true)
908 }
909
910 Break(_, Some(ref value)) => {
911 (value, UnusedDelimsCtx::BreakValue, false, None, None, true)
912 }
913
914 Index(_, ref value, _) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false),
915
916 Assign(_, ref value, _) | AssignOp(.., ref value) => {
917 (value, UnusedDelimsCtx::AssignedValue, false, None, None, false)
918 }
919 ref call_or_other => {
921 let (args_to_check, ctx) = match *call_or_other {
922 Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg),
923 MethodCall(ref call) => (&call.args[..], UnusedDelimsCtx::MethodArg),
924 Closure(ref closure)
925 if matches!(closure.fn_decl.output, FnRetTy::Default(_)) =>
926 {
927 (&[closure.body.clone()][..], UnusedDelimsCtx::ClosureBody)
928 }
929 _ => {
931 return;
932 }
933 };
934 if e.span.ctxt().outer_expn_data().call_site.from_expansion() {
939 return;
940 }
941 for arg in args_to_check {
942 self.check_unused_delims_expr(cx, arg, ctx, false, None, None, false);
943 }
944 return;
945 }
946 };
947 self.check_unused_delims_expr(
948 cx,
949 value,
950 ctx,
951 followed_by_block,
952 left_pos,
953 right_pos,
954 is_kw,
955 );
956 }
957
958 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
959 match s.kind {
960 StmtKind::Let(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => {
961 if let Some((init, els)) = local.kind.init_else_opt() {
962 if els.is_some()
963 && let ExprKind::Paren(paren) = &init.kind
964 && !init.span.eq_ctxt(paren.span)
965 {
966 return;
977 }
978 let ctx = match els {
979 None => UnusedDelimsCtx::AssignedValue,
980 Some(_) => UnusedDelimsCtx::AssignedValueLetElse,
981 };
982 self.check_unused_delims_expr(cx, init, ctx, false, None, None, false);
983 }
984 }
985 StmtKind::Expr(ref expr) => {
986 self.check_unused_delims_expr(
987 cx,
988 expr,
989 UnusedDelimsCtx::BlockRetValue,
990 false,
991 None,
992 None,
993 false,
994 );
995 }
996 _ => {}
997 }
998 }
999
1000 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1001 use ast::ItemKind::*;
1002
1003 if let Const(box ast::ConstItem { expr: Some(expr), .. })
1004 | Static(box ast::StaticItem { expr: Some(expr), .. }) = &item.kind
1005 {
1006 self.check_unused_delims_expr(
1007 cx,
1008 expr,
1009 UnusedDelimsCtx::AssignedValue,
1010 false,
1011 None,
1012 None,
1013 false,
1014 );
1015 }
1016 }
1017}
1018
1019declare_lint! {
1020 pub(super) UNUSED_PARENS,
1036 Warn,
1037 "`if`, `match`, `while` and `return` do not need parentheses"
1038}
1039
1040#[derive(Default)]
1041pub(crate) struct UnusedParens {
1042 with_self_ty_parens: bool,
1043 parens_in_cast_in_lt: Vec<ast::NodeId>,
1046 in_no_bounds_pos: FxHashMap<ast::NodeId, NoBoundsException>,
1049}
1050
1051enum NoBoundsException {
1066 None,
1068 OneBound,
1071}
1072
1073impl_lint_pass!(UnusedParens => [UNUSED_PARENS]);
1074
1075impl UnusedDelimLint for UnusedParens {
1076 const DELIM_STR: &'static str = "parentheses";
1077
1078 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = true;
1079
1080 fn lint(&self) -> &'static Lint {
1081 UNUSED_PARENS
1082 }
1083
1084 fn check_unused_delims_expr(
1085 &self,
1086 cx: &EarlyContext<'_>,
1087 value: &ast::Expr,
1088 ctx: UnusedDelimsCtx,
1089 followed_by_block: bool,
1090 left_pos: Option<BytePos>,
1091 right_pos: Option<BytePos>,
1092 is_kw: bool,
1093 ) {
1094 match value.kind {
1095 ast::ExprKind::Paren(ref inner) => {
1096 if !Self::is_expr_delims_necessary(inner, ctx, followed_by_block)
1097 && value.attrs.is_empty()
1098 && !value.span.from_expansion()
1099 && (ctx != UnusedDelimsCtx::LetScrutineeExpr
1100 || !matches!(inner.kind, ast::ExprKind::Binary(
1101 rustc_span::source_map::Spanned { node, .. },
1102 _,
1103 _,
1104 ) if node.is_lazy()))
1105 && !((ctx == UnusedDelimsCtx::ReturnValue
1106 || ctx == UnusedDelimsCtx::BreakValue)
1107 && matches!(inner.kind, ast::ExprKind::Assign(_, _, _)))
1108 {
1109 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1110 }
1111 }
1112 ast::ExprKind::Let(_, ref expr, _, _) => {
1113 self.check_unused_delims_expr(
1114 cx,
1115 expr,
1116 UnusedDelimsCtx::LetScrutineeExpr,
1117 followed_by_block,
1118 None,
1119 None,
1120 false,
1121 );
1122 }
1123 _ => {}
1124 }
1125 }
1126}
1127
1128impl UnusedParens {
1129 fn check_unused_parens_pat(
1130 &self,
1131 cx: &EarlyContext<'_>,
1132 value: &ast::Pat,
1133 avoid_or: bool,
1134 avoid_mut: bool,
1135 keep_space: (bool, bool),
1136 ) {
1137 use ast::{BindingMode, PatKind};
1138
1139 if let PatKind::Paren(inner) = &value.kind {
1140 match inner.kind {
1141 PatKind::Range(..) => return,
1146 PatKind::Or(..) if avoid_or => return,
1148 PatKind::Ident(BindingMode::MUT, ..) if avoid_mut => {
1150 return;
1151 }
1152 _ => {}
1154 }
1155 let spans = if !value.span.from_expansion() {
1156 inner
1157 .span
1158 .find_ancestor_inside(value.span)
1159 .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi())))
1160 } else {
1161 None
1162 };
1163 self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false);
1164 }
1165 }
1166
1167 fn cast_followed_by_lt(&self, expr: &ast::Expr) -> Option<ast::NodeId> {
1168 if let ExprKind::Binary(op, lhs, _rhs) = &expr.kind
1169 && (op.node == ast::BinOpKind::Lt || op.node == ast::BinOpKind::Shl)
1170 {
1171 let mut cur = lhs;
1172 while let ExprKind::Binary(_, _, rhs) = &cur.kind {
1173 cur = rhs;
1174 }
1175
1176 if let ExprKind::Cast(_, ty) = &cur.kind
1177 && let ast::TyKind::Paren(_) = &ty.kind
1178 {
1179 return Some(ty.id);
1180 }
1181 }
1182 None
1183 }
1184}
1185
1186impl EarlyLintPass for UnusedParens {
1187 #[inline]
1188 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1189 if let Some(ty_id) = self.cast_followed_by_lt(e) {
1190 self.parens_in_cast_in_lt.push(ty_id);
1191 }
1192
1193 match e.kind {
1194 ExprKind::Let(ref pat, _, _, _) | ExprKind::ForLoop { ref pat, .. } => {
1195 self.check_unused_parens_pat(cx, pat, false, false, (true, true));
1196 }
1197 ExprKind::If(ref cond, ref block, ref else_)
1201 if matches!(cond.peel_parens().kind, ExprKind::Let(..)) =>
1202 {
1203 self.check_unused_delims_expr(
1204 cx,
1205 cond.peel_parens(),
1206 UnusedDelimsCtx::LetScrutineeExpr,
1207 true,
1208 None,
1209 None,
1210 true,
1211 );
1212 for stmt in &block.stmts {
1213 <Self as UnusedDelimLint>::check_stmt(self, cx, stmt);
1214 }
1215 if let Some(e) = else_ {
1216 <Self as UnusedDelimLint>::check_expr(self, cx, e);
1217 }
1218 return;
1219 }
1220 ExprKind::Match(ref _expr, ref arm, _) => {
1221 for a in arm {
1222 if let Some(body) = &a.body {
1223 self.check_unused_delims_expr(
1224 cx,
1225 body,
1226 UnusedDelimsCtx::MatchArmExpr,
1227 false,
1228 None,
1229 None,
1230 true,
1231 );
1232 }
1233 }
1234 }
1235 _ => {}
1236 }
1237
1238 <Self as UnusedDelimLint>::check_expr(self, cx, e)
1239 }
1240
1241 fn check_expr_post(&mut self, _cx: &EarlyContext<'_>, e: &ast::Expr) {
1242 if let Some(ty_id) = self.cast_followed_by_lt(e) {
1243 let id = self
1244 .parens_in_cast_in_lt
1245 .pop()
1246 .expect("check_expr and check_expr_post must balance");
1247 assert_eq!(
1248 id, ty_id,
1249 "check_expr, check_ty, and check_expr_post are called, in that order, by the visitor"
1250 );
1251 }
1252 }
1253
1254 fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) {
1255 use ast::Mutability;
1256 use ast::PatKind::*;
1257 let keep_space = (false, false);
1258 match &p.kind {
1259 Paren(_)
1261 | Missing | Wild | Never | Rest | Expr(..) | MacCall(..) | Range(..) | Ident(.., None)
1263 | Path(..) | Err(_) => {},
1264 TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps {
1266 self.check_unused_parens_pat(cx, p, false, false, keep_space);
1267 },
1268 Struct(_, _, fps, _) => for f in fps {
1269 self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space);
1270 },
1271 Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space),
1273 Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space),
1276 }
1277 }
1278
1279 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1280 if let StmtKind::Let(ref local) = s.kind {
1281 self.check_unused_parens_pat(cx, &local.pat, true, false, (true, false));
1282 }
1283
1284 <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1285 }
1286
1287 fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) {
1288 self.check_unused_parens_pat(cx, ¶m.pat, true, false, (false, false));
1289 }
1290
1291 fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) {
1292 self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false));
1293 }
1294
1295 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1296 if let ast::TyKind::Paren(_) = ty.kind
1297 && Some(&ty.id) == self.parens_in_cast_in_lt.last()
1298 {
1299 return;
1300 }
1301 match &ty.kind {
1302 ast::TyKind::Array(_, len) => {
1303 self.check_unused_delims_expr(
1304 cx,
1305 &len.value,
1306 UnusedDelimsCtx::ArrayLenExpr,
1307 false,
1308 None,
1309 None,
1310 false,
1311 );
1312 }
1313 ast::TyKind::Paren(r) => {
1314 let unused_parens = match &r.kind {
1315 ast::TyKind::ImplTrait(_, bounds) | ast::TyKind::TraitObject(bounds, _) => {
1316 match self.in_no_bounds_pos.get(&ty.id) {
1317 Some(NoBoundsException::None) => false,
1318 Some(NoBoundsException::OneBound) => bounds.len() <= 1,
1319 None => true,
1320 }
1321 }
1322 ast::TyKind::FnPtr(b) => {
1323 !self.with_self_ty_parens || b.generic_params.is_empty()
1324 }
1325 _ => true,
1326 };
1327
1328 if unused_parens {
1329 let spans = (!ty.span.from_expansion())
1330 .then(|| {
1331 r.span
1332 .find_ancestor_inside(ty.span)
1333 .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi())))
1334 })
1335 .flatten();
1336
1337 self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false);
1338 }
1339
1340 self.with_self_ty_parens = false;
1341 }
1342 ast::TyKind::Ref(_, mut_ty) | ast::TyKind::Ptr(mut_ty) => {
1343 self.in_no_bounds_pos.insert(mut_ty.ty.id, NoBoundsException::OneBound);
1344 }
1345 ast::TyKind::TraitObject(bounds, _) | ast::TyKind::ImplTrait(_, bounds) => {
1346 for i in 0..bounds.len() {
1347 let is_last = i == bounds.len() - 1;
1348
1349 if let ast::GenericBound::Trait(poly_trait_ref) = &bounds[i] {
1350 let fn_with_explicit_ret_ty = if let [.., segment] =
1351 &*poly_trait_ref.trait_ref.path.segments
1352 && let Some(args) = segment.args.as_ref()
1353 && let ast::GenericArgs::Parenthesized(paren_args) = &**args
1354 && let ast::FnRetTy::Ty(ret_ty) = &paren_args.output
1355 {
1356 self.in_no_bounds_pos.insert(
1357 ret_ty.id,
1358 if is_last {
1359 NoBoundsException::OneBound
1360 } else {
1361 NoBoundsException::None
1362 },
1363 );
1364
1365 true
1366 } else {
1367 false
1368 };
1369
1370 let dyn2015_exception = cx.sess().psess.edition == Edition2015
1375 && matches!(ty.kind, ast::TyKind::TraitObject(..))
1376 && i == 0
1377 && poly_trait_ref
1378 .trait_ref
1379 .path
1380 .segments
1381 .first()
1382 .map(|s| s.ident.name == kw::PathRoot)
1383 .unwrap_or(false);
1384
1385 if let ast::Parens::Yes = poly_trait_ref.parens
1386 && (is_last || !fn_with_explicit_ret_ty)
1387 && !dyn2015_exception
1388 {
1389 let s = poly_trait_ref.span;
1390 let spans = (!s.from_expansion()).then(|| {
1391 (
1392 s.with_hi(s.lo() + rustc_span::BytePos(1)),
1393 s.with_lo(s.hi() - rustc_span::BytePos(1)),
1394 )
1395 });
1396
1397 self.emit_unused_delims(
1398 cx,
1399 poly_trait_ref.span,
1400 spans,
1401 "type",
1402 (false, false),
1403 false,
1404 );
1405 }
1406 }
1407 }
1408 }
1409 _ => {}
1410 }
1411 }
1412
1413 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1414 <Self as UnusedDelimLint>::check_item(self, cx, item)
1415 }
1416
1417 fn check_item_post(&mut self, _: &EarlyContext<'_>, _: &rustc_ast::Item) {
1418 self.in_no_bounds_pos.clear();
1419 }
1420
1421 fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) {
1422 use rustc_ast::{WhereBoundPredicate, WherePredicateKind};
1423 if let WherePredicateKind::BoundPredicate(WhereBoundPredicate {
1424 bounded_ty,
1425 bound_generic_params,
1426 ..
1427 }) = &pred.kind
1428 && let ast::TyKind::Paren(_) = &bounded_ty.kind
1429 && bound_generic_params.is_empty()
1430 {
1431 self.with_self_ty_parens = true;
1432 }
1433 }
1434
1435 fn exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate) {
1436 assert!(!self.with_self_ty_parens);
1437 }
1438}
1439
1440declare_lint! {
1441 pub(super) UNUSED_BRACES,
1459 Warn,
1460 "unnecessary braces around an expression"
1461}
1462
1463declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]);
1464
1465impl UnusedDelimLint for UnusedBraces {
1466 const DELIM_STR: &'static str = "braces";
1467
1468 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = false;
1469
1470 fn lint(&self) -> &'static Lint {
1471 UNUSED_BRACES
1472 }
1473
1474 fn check_unused_delims_expr(
1475 &self,
1476 cx: &EarlyContext<'_>,
1477 value: &ast::Expr,
1478 ctx: UnusedDelimsCtx,
1479 followed_by_block: bool,
1480 left_pos: Option<BytePos>,
1481 right_pos: Option<BytePos>,
1482 is_kw: bool,
1483 ) {
1484 match value.kind {
1485 ast::ExprKind::Block(ref inner, None)
1486 if inner.rules == ast::BlockCheckMode::Default =>
1487 {
1488 if let [stmt] = inner.stmts.as_slice() {
1513 if let ast::StmtKind::Expr(ref expr) = stmt.kind {
1514 if !Self::is_expr_delims_necessary(expr, ctx, followed_by_block)
1515 && (ctx != UnusedDelimsCtx::AnonConst
1516 || (matches!(expr.kind, ast::ExprKind::Lit(_))
1517 && !expr.span.from_expansion()))
1518 && ctx != UnusedDelimsCtx::ClosureBody
1519 && !cx.sess().source_map().is_multiline(value.span)
1520 && value.attrs.is_empty()
1521 && !value.span.from_expansion()
1522 && !inner.span.from_expansion()
1523 {
1524 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw)
1525 }
1526 }
1527 }
1528 }
1529 ast::ExprKind::Let(_, ref expr, _, _) => {
1530 self.check_unused_delims_expr(
1531 cx,
1532 expr,
1533 UnusedDelimsCtx::LetScrutineeExpr,
1534 followed_by_block,
1535 None,
1536 None,
1537 false,
1538 );
1539 }
1540 _ => {}
1541 }
1542 }
1543}
1544
1545impl EarlyLintPass for UnusedBraces {
1546 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) {
1547 <Self as UnusedDelimLint>::check_stmt(self, cx, s)
1548 }
1549
1550 #[inline]
1551 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
1552 <Self as UnusedDelimLint>::check_expr(self, cx, e);
1553
1554 if let ExprKind::Repeat(_, ref anon_const) = e.kind {
1555 self.check_unused_delims_expr(
1556 cx,
1557 &anon_const.value,
1558 UnusedDelimsCtx::AnonConst,
1559 false,
1560 None,
1561 None,
1562 false,
1563 );
1564 }
1565 }
1566
1567 fn check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg) {
1568 if let ast::GenericArg::Const(ct) = arg {
1569 self.check_unused_delims_expr(
1570 cx,
1571 &ct.value,
1572 UnusedDelimsCtx::AnonConst,
1573 false,
1574 None,
1575 None,
1576 false,
1577 );
1578 }
1579 }
1580
1581 fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &ast::Variant) {
1582 if let Some(anon_const) = &v.disr_expr {
1583 self.check_unused_delims_expr(
1584 cx,
1585 &anon_const.value,
1586 UnusedDelimsCtx::AnonConst,
1587 false,
1588 None,
1589 None,
1590 false,
1591 );
1592 }
1593 }
1594
1595 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) {
1596 match ty.kind {
1597 ast::TyKind::Array(_, ref len) => {
1598 self.check_unused_delims_expr(
1599 cx,
1600 &len.value,
1601 UnusedDelimsCtx::ArrayLenExpr,
1602 false,
1603 None,
1604 None,
1605 false,
1606 );
1607 }
1608
1609 ast::TyKind::Typeof(ref anon_const) => {
1610 self.check_unused_delims_expr(
1611 cx,
1612 &anon_const.value,
1613 UnusedDelimsCtx::AnonConst,
1614 false,
1615 None,
1616 None,
1617 false,
1618 );
1619 }
1620
1621 _ => {}
1622 }
1623 }
1624
1625 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1626 <Self as UnusedDelimLint>::check_item(self, cx, item)
1627 }
1628}
1629
1630declare_lint! {
1631 UNUSED_IMPORT_BRACES,
1656 Allow,
1657 "unnecessary braces around an imported item"
1658}
1659
1660declare_lint_pass!(UnusedImportBraces => [UNUSED_IMPORT_BRACES]);
1661
1662impl UnusedImportBraces {
1663 fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) {
1664 if let ast::UseTreeKind::Nested { ref items, .. } = use_tree.kind {
1665 for (tree, _) in items {
1667 self.check_use_tree(cx, tree, item);
1668 }
1669
1670 let [(tree, _)] = items.as_slice() else { return };
1672
1673 let node_name = match tree.kind {
1675 ast::UseTreeKind::Simple(rename) => {
1676 let orig_ident = tree.prefix.segments.last().unwrap().ident;
1677 if orig_ident.name == kw::SelfLower {
1678 return;
1679 }
1680 rename.unwrap_or(orig_ident).name
1681 }
1682 ast::UseTreeKind::Glob => sym::asterisk,
1683 ast::UseTreeKind::Nested { .. } => return,
1684 };
1685
1686 cx.emit_span_lint(
1687 UNUSED_IMPORT_BRACES,
1688 item.span,
1689 UnusedImportBracesDiag { node: node_name },
1690 );
1691 }
1692 }
1693}
1694
1695impl EarlyLintPass for UnusedImportBraces {
1696 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
1697 if let ast::ItemKind::Use(ref use_tree) = item.kind {
1698 self.check_use_tree(cx, use_tree, item);
1699 }
1700 }
1701}
1702
1703declare_lint! {
1704 pub(super) UNUSED_ALLOCATION,
1723 Warn,
1724 "detects unnecessary allocations that can be eliminated"
1725}
1726
1727declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]);
1728
1729impl<'tcx> LateLintPass<'tcx> for UnusedAllocation {
1730 fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
1731 match e.kind {
1732 hir::ExprKind::Call(path_expr, [_])
1733 if let hir::ExprKind::Path(qpath) = &path_expr.kind
1734 && let Some(did) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id()
1735 && cx.tcx.is_diagnostic_item(sym::box_new, did) => {}
1736 _ => return,
1737 }
1738
1739 for adj in cx.typeck_results().expr_adjustments(e) {
1740 if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(m)) = adj.kind {
1741 match m {
1742 adjustment::AutoBorrowMutability::Not => {
1743 cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag);
1744 }
1745 adjustment::AutoBorrowMutability::Mut { .. } => {
1746 cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag);
1747 }
1748 };
1749 }
1750 }
1751 }
1752}