1 use crate::lints::{ 2 PathStatementDrop, PathStatementDropSub, PathStatementNoEffect, UnusedAllocationDiag, 3 UnusedAllocationMutDiag, UnusedClosure, UnusedDef, UnusedDefSuggestion, UnusedDelim, 4 UnusedDelimSuggestion, UnusedGenerator, UnusedImportBracesDiag, UnusedOp, UnusedOpSuggestion, 5 UnusedResult, 6 }; 7 use crate::Lint; 8 use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; 9 use rustc_ast as ast; 10 use rustc_ast::util::{classify, parser}; 11 use rustc_ast::{ExprKind, StmtKind}; 12 use rustc_errors::{pluralize, MultiSpan}; 13 use rustc_hir as hir; 14 use rustc_hir::def::{DefKind, Res}; 15 use rustc_hir::def_id::DefId; 16 use rustc_infer::traits::util::elaborate; 17 use rustc_middle::ty::adjustment; 18 use rustc_middle::ty::{self, Ty}; 19 use rustc_span::symbol::Symbol; 20 use rustc_span::symbol::{kw, sym}; 21 use rustc_span::{BytePos, Span}; 22 use std::iter; 23 24 declare_lint! { 25 /// The `unused_must_use` lint detects unused result of a type flagged as 26 /// `#[must_use]`. 27 /// 28 /// ### Example 29 /// 30 /// ```rust 31 /// fn returns_result() -> Result<(), ()> { 32 /// Ok(()) 33 /// } 34 /// 35 /// fn main() { 36 /// returns_result(); 37 /// } 38 /// ``` 39 /// 40 /// {{produces}} 41 /// 42 /// ### Explanation 43 /// 44 /// The `#[must_use]` attribute is an indicator that it is a mistake to 45 /// ignore the value. See [the reference] for more details. 46 /// 47 /// [the reference]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute 48 pub UNUSED_MUST_USE, 49 Warn, 50 "unused result of a type flagged as `#[must_use]`", 51 report_in_external_macro 52 } 53 54 declare_lint! { 55 /// The `unused_results` lint checks for the unused result of an 56 /// expression in a statement. 57 /// 58 /// ### Example 59 /// 60 /// ```rust,compile_fail 61 /// #![deny(unused_results)] 62 /// fn foo<T>() -> T { panic!() } 63 /// 64 /// fn main() { 65 /// foo::<usize>(); 66 /// } 67 /// ``` 68 /// 69 /// {{produces}} 70 /// 71 /// ### Explanation 72 /// 73 /// Ignoring the return value of a function may indicate a mistake. In 74 /// cases were it is almost certain that the result should be used, it is 75 /// recommended to annotate the function with the [`must_use` attribute]. 76 /// Failure to use such a return value will trigger the [`unused_must_use` 77 /// lint] which is warn-by-default. The `unused_results` lint is 78 /// essentially the same, but triggers for *all* return values. 79 /// 80 /// This lint is "allow" by default because it can be noisy, and may not be 81 /// an actual problem. For example, calling the `remove` method of a `Vec` 82 /// or `HashMap` returns the previous value, which you may not care about. 83 /// Using this lint would require explicitly ignoring or discarding such 84 /// values. 85 /// 86 /// [`must_use` attribute]: https://doc.rust-lang.org/reference/attributes/diagnostics.html#the-must_use-attribute 87 /// [`unused_must_use` lint]: warn-by-default.html#unused-must-use 88 pub UNUSED_RESULTS, 89 Allow, 90 "unused result of an expression in a statement" 91 } 92 93 declare_lint_pass!(UnusedResults => [UNUSED_MUST_USE, UNUSED_RESULTS]); 94 95 impl<'tcx> LateLintPass<'tcx> for UnusedResults { check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>)96 fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { 97 let hir::StmtKind::Semi(mut expr) = s.kind else { return; }; 98 99 let mut expr_is_from_block = false; 100 while let hir::ExprKind::Block(blk, ..) = expr.kind 101 && let hir::Block { expr: Some(e), .. } = blk 102 { 103 expr = e; 104 expr_is_from_block = true; 105 } 106 107 if let hir::ExprKind::Ret(..) = expr.kind { 108 return; 109 } 110 111 if let hir::ExprKind::Match(await_expr, _arms, hir::MatchSource::AwaitDesugar) = expr.kind 112 && let ty = cx.typeck_results().expr_ty(&await_expr) 113 && let ty::Alias(ty::Opaque, ty::AliasTy { def_id: future_def_id, .. }) = ty.kind() 114 && cx.tcx.ty_is_opaque_future(ty) 115 && let async_fn_def_id = cx.tcx.parent(*future_def_id) 116 && matches!(cx.tcx.def_kind(async_fn_def_id), DefKind::Fn | DefKind::AssocFn) 117 // Check that this `impl Future` actually comes from an `async fn` 118 && cx.tcx.asyncness(async_fn_def_id).is_async() 119 && check_must_use_def( 120 cx, 121 async_fn_def_id, 122 expr.span, 123 "output of future returned by ", 124 "", 125 expr_is_from_block, 126 ) 127 { 128 // We have a bare `foo().await;` on an opaque type from an async function that was 129 // annotated with `#[must_use]`. 130 return; 131 } 132 133 let ty = cx.typeck_results().expr_ty(&expr); 134 135 let must_use_result = is_ty_must_use(cx, ty, &expr, expr.span); 136 let type_lint_emitted_or_suppressed = match must_use_result { 137 Some(path) => { 138 emit_must_use_untranslated(cx, &path, "", "", 1, false, expr_is_from_block); 139 true 140 } 141 None => false, 142 }; 143 144 let fn_warned = check_fn_must_use(cx, expr, expr_is_from_block); 145 146 if !fn_warned && type_lint_emitted_or_suppressed { 147 // We don't warn about unused unit or uninhabited types. 148 // (See https://github.com/rust-lang/rust/issues/43806 for details.) 149 return; 150 } 151 152 let must_use_op = match expr.kind { 153 // Hardcoding operators here seemed more expedient than the 154 // refactoring that would be needed to look up the `#[must_use]` 155 // attribute which does exist on the comparison trait methods 156 hir::ExprKind::Binary(bin_op, ..) => match bin_op.node { 157 hir::BinOpKind::Eq 158 | hir::BinOpKind::Lt 159 | hir::BinOpKind::Le 160 | hir::BinOpKind::Ne 161 | hir::BinOpKind::Ge 162 | hir::BinOpKind::Gt => Some("comparison"), 163 hir::BinOpKind::Add 164 | hir::BinOpKind::Sub 165 | hir::BinOpKind::Div 166 | hir::BinOpKind::Mul 167 | hir::BinOpKind::Rem => Some("arithmetic operation"), 168 hir::BinOpKind::And | hir::BinOpKind::Or => Some("logical operation"), 169 hir::BinOpKind::BitXor 170 | hir::BinOpKind::BitAnd 171 | hir::BinOpKind::BitOr 172 | hir::BinOpKind::Shl 173 | hir::BinOpKind::Shr => Some("bitwise operation"), 174 }, 175 hir::ExprKind::AddrOf(..) => Some("borrow"), 176 hir::ExprKind::Unary(..) => Some("unary operation"), 177 _ => None, 178 }; 179 180 let mut op_warned = false; 181 182 if let Some(must_use_op) = must_use_op { 183 cx.emit_spanned_lint( 184 UNUSED_MUST_USE, 185 expr.span, 186 UnusedOp { 187 op: must_use_op, 188 label: expr.span, 189 suggestion: if expr_is_from_block { 190 UnusedOpSuggestion::BlockTailExpr { 191 before_span: expr.span.shrink_to_lo(), 192 after_span: expr.span.shrink_to_hi(), 193 } 194 } else { 195 UnusedOpSuggestion::NormalExpr { span: expr.span.shrink_to_lo() } 196 }, 197 }, 198 ); 199 op_warned = true; 200 } 201 202 if !(type_lint_emitted_or_suppressed || fn_warned || op_warned) { 203 cx.emit_spanned_lint(UNUSED_RESULTS, s.span, UnusedResult { ty }); 204 } 205 206 fn check_fn_must_use( 207 cx: &LateContext<'_>, 208 expr: &hir::Expr<'_>, 209 expr_is_from_block: bool, 210 ) -> bool { 211 let maybe_def_id = match expr.kind { 212 hir::ExprKind::Call(ref callee, _) => { 213 match callee.kind { 214 hir::ExprKind::Path(ref qpath) => { 215 match cx.qpath_res(qpath, callee.hir_id) { 216 Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => Some(def_id), 217 // `Res::Local` if it was a closure, for which we 218 // do not currently support must-use linting 219 _ => None, 220 } 221 } 222 _ => None, 223 } 224 } 225 hir::ExprKind::MethodCall(..) => { 226 cx.typeck_results().type_dependent_def_id(expr.hir_id) 227 } 228 _ => None, 229 }; 230 if let Some(def_id) = maybe_def_id { 231 check_must_use_def( 232 cx, 233 def_id, 234 expr.span, 235 "return value of ", 236 "", 237 expr_is_from_block, 238 ) 239 } else { 240 false 241 } 242 } 243 244 /// A path through a type to a must_use source. Contains useful info for the lint. 245 #[derive(Debug)] 246 enum MustUsePath { 247 /// Suppress must_use checking. 248 Suppressed, 249 /// The root of the normal must_use lint with an optional message. 250 Def(Span, DefId, Option<Symbol>), 251 Boxed(Box<Self>), 252 Opaque(Box<Self>), 253 TraitObject(Box<Self>), 254 TupleElement(Vec<(usize, Self)>), 255 Array(Box<Self>, u64), 256 /// The root of the unused_closures lint. 257 Closure(Span), 258 /// The root of the unused_generators lint. 259 Generator(Span), 260 } 261 262 #[instrument(skip(cx, expr), level = "debug", ret)] 263 fn is_ty_must_use<'tcx>( 264 cx: &LateContext<'tcx>, 265 ty: Ty<'tcx>, 266 expr: &hir::Expr<'_>, 267 span: Span, 268 ) -> Option<MustUsePath> { 269 if ty.is_unit() 270 || !ty.is_inhabited_from( 271 cx.tcx, 272 cx.tcx.parent_module(expr.hir_id).to_def_id(), 273 cx.param_env, 274 ) 275 { 276 return Some(MustUsePath::Suppressed); 277 } 278 279 match *ty.kind() { 280 ty::Adt(..) if ty.is_box() => { 281 let boxed_ty = ty.boxed_ty(); 282 is_ty_must_use(cx, boxed_ty, expr, span) 283 .map(|inner| MustUsePath::Boxed(Box::new(inner))) 284 } 285 ty::Adt(def, _) => is_def_must_use(cx, def.did(), span), 286 ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => { 287 elaborate(cx.tcx, cx.tcx.explicit_item_bounds(def).subst_identity_iter_copied()) 288 // We only care about self bounds for the impl-trait 289 .filter_only_self() 290 .find_map(|(pred, _span)| { 291 // We only look at the `DefId`, so it is safe to skip the binder here. 292 if let ty::ClauseKind::Trait(ref poly_trait_predicate) = 293 pred.kind().skip_binder() 294 { 295 let def_id = poly_trait_predicate.trait_ref.def_id; 296 297 is_def_must_use(cx, def_id, span) 298 } else { 299 None 300 } 301 }) 302 .map(|inner| MustUsePath::Opaque(Box::new(inner))) 303 } 304 ty::Dynamic(binders, _, _) => binders.iter().find_map(|predicate| { 305 if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() 306 { 307 let def_id = trait_ref.def_id; 308 is_def_must_use(cx, def_id, span) 309 .map(|inner| MustUsePath::TraitObject(Box::new(inner))) 310 } else { 311 None 312 } 313 }), 314 ty::Tuple(tys) => { 315 let elem_exprs = if let hir::ExprKind::Tup(elem_exprs) = expr.kind { 316 debug_assert_eq!(elem_exprs.len(), tys.len()); 317 elem_exprs 318 } else { 319 &[] 320 }; 321 322 // Default to `expr`. 323 let elem_exprs = elem_exprs.iter().chain(iter::repeat(expr)); 324 325 let nested_must_use = tys 326 .iter() 327 .zip(elem_exprs) 328 .enumerate() 329 .filter_map(|(i, (ty, expr))| { 330 is_ty_must_use(cx, ty, expr, expr.span).map(|path| (i, path)) 331 }) 332 .collect::<Vec<_>>(); 333 334 if !nested_must_use.is_empty() { 335 Some(MustUsePath::TupleElement(nested_must_use)) 336 } else { 337 None 338 } 339 } 340 ty::Array(ty, len) => match len.try_eval_target_usize(cx.tcx, cx.param_env) { 341 // If the array is empty we don't lint, to avoid false positives 342 Some(0) | None => None, 343 // If the array is definitely non-empty, we can do `#[must_use]` checking. 344 Some(len) => is_ty_must_use(cx, ty, expr, span) 345 .map(|inner| MustUsePath::Array(Box::new(inner), len)), 346 }, 347 ty::Closure(..) => Some(MustUsePath::Closure(span)), 348 ty::Generator(def_id, ..) => { 349 // async fn should be treated as "implementor of `Future`" 350 let must_use = if cx.tcx.generator_is_async(def_id) { 351 let def_id = cx.tcx.lang_items().future_trait().unwrap(); 352 is_def_must_use(cx, def_id, span) 353 .map(|inner| MustUsePath::Opaque(Box::new(inner))) 354 } else { 355 None 356 }; 357 must_use.or(Some(MustUsePath::Generator(span))) 358 } 359 _ => None, 360 } 361 } 362 363 fn is_def_must_use(cx: &LateContext<'_>, def_id: DefId, span: Span) -> Option<MustUsePath> { 364 if let Some(attr) = cx.tcx.get_attr(def_id, sym::must_use) { 365 // check for #[must_use = "..."] 366 let reason = attr.value_str(); 367 Some(MustUsePath::Def(span, def_id, reason)) 368 } else { 369 None 370 } 371 } 372 373 // Returns whether further errors should be suppressed because either a lint has been emitted or the type should be ignored. 374 fn check_must_use_def( 375 cx: &LateContext<'_>, 376 def_id: DefId, 377 span: Span, 378 descr_pre_path: &str, 379 descr_post_path: &str, 380 expr_is_from_block: bool, 381 ) -> bool { 382 is_def_must_use(cx, def_id, span) 383 .map(|must_use_path| { 384 emit_must_use_untranslated( 385 cx, 386 &must_use_path, 387 descr_pre_path, 388 descr_post_path, 389 1, 390 false, 391 expr_is_from_block, 392 ) 393 }) 394 .is_some() 395 } 396 397 #[instrument(skip(cx), level = "debug")] 398 fn emit_must_use_untranslated( 399 cx: &LateContext<'_>, 400 path: &MustUsePath, 401 descr_pre: &str, 402 descr_post: &str, 403 plural_len: usize, 404 is_inner: bool, 405 expr_is_from_block: bool, 406 ) { 407 let plural_suffix = pluralize!(plural_len); 408 409 match path { 410 MustUsePath::Suppressed => {} 411 MustUsePath::Boxed(path) => { 412 let descr_pre = &format!("{}boxed ", descr_pre); 413 emit_must_use_untranslated( 414 cx, 415 path, 416 descr_pre, 417 descr_post, 418 plural_len, 419 true, 420 expr_is_from_block, 421 ); 422 } 423 MustUsePath::Opaque(path) => { 424 let descr_pre = &format!("{}implementer{} of ", descr_pre, plural_suffix); 425 emit_must_use_untranslated( 426 cx, 427 path, 428 descr_pre, 429 descr_post, 430 plural_len, 431 true, 432 expr_is_from_block, 433 ); 434 } 435 MustUsePath::TraitObject(path) => { 436 let descr_post = &format!(" trait object{}{}", plural_suffix, descr_post); 437 emit_must_use_untranslated( 438 cx, 439 path, 440 descr_pre, 441 descr_post, 442 plural_len, 443 true, 444 expr_is_from_block, 445 ); 446 } 447 MustUsePath::TupleElement(elems) => { 448 for (index, path) in elems { 449 let descr_post = &format!(" in tuple element {}", index); 450 emit_must_use_untranslated( 451 cx, 452 path, 453 descr_pre, 454 descr_post, 455 plural_len, 456 true, 457 expr_is_from_block, 458 ); 459 } 460 } 461 MustUsePath::Array(path, len) => { 462 let descr_pre = &format!("{}array{} of ", descr_pre, plural_suffix); 463 emit_must_use_untranslated( 464 cx, 465 path, 466 descr_pre, 467 descr_post, 468 plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)), 469 true, 470 expr_is_from_block, 471 ); 472 } 473 MustUsePath::Closure(span) => { 474 cx.emit_spanned_lint( 475 UNUSED_MUST_USE, 476 *span, 477 UnusedClosure { count: plural_len, pre: descr_pre, post: descr_post }, 478 ); 479 } 480 MustUsePath::Generator(span) => { 481 cx.emit_spanned_lint( 482 UNUSED_MUST_USE, 483 *span, 484 UnusedGenerator { count: plural_len, pre: descr_pre, post: descr_post }, 485 ); 486 } 487 MustUsePath::Def(span, def_id, reason) => { 488 cx.emit_spanned_lint( 489 UNUSED_MUST_USE, 490 *span, 491 UnusedDef { 492 pre: descr_pre, 493 post: descr_post, 494 cx, 495 def_id: *def_id, 496 note: *reason, 497 suggestion: (!is_inner).then_some(if expr_is_from_block { 498 UnusedDefSuggestion::BlockTailExpr { 499 before_span: span.shrink_to_lo(), 500 after_span: span.shrink_to_hi(), 501 } 502 } else { 503 UnusedDefSuggestion::NormalExpr { span: span.shrink_to_lo() } 504 }), 505 }, 506 ); 507 } 508 } 509 } 510 } 511 } 512 513 declare_lint! { 514 /// The `path_statements` lint detects path statements with no effect. 515 /// 516 /// ### Example 517 /// 518 /// ```rust 519 /// let x = 42; 520 /// 521 /// x; 522 /// ``` 523 /// 524 /// {{produces}} 525 /// 526 /// ### Explanation 527 /// 528 /// It is usually a mistake to have a statement that has no effect. 529 pub PATH_STATEMENTS, 530 Warn, 531 "path statements with no effect" 532 } 533 534 declare_lint_pass!(PathStatements => [PATH_STATEMENTS]); 535 536 impl<'tcx> LateLintPass<'tcx> for PathStatements { check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>)537 fn check_stmt(&mut self, cx: &LateContext<'_>, s: &hir::Stmt<'_>) { 538 if let hir::StmtKind::Semi(expr) = s.kind { 539 if let hir::ExprKind::Path(_) = expr.kind { 540 let ty = cx.typeck_results().expr_ty(expr); 541 if ty.needs_drop(cx.tcx, cx.param_env) { 542 let sub = if let Ok(snippet) = cx.sess().source_map().span_to_snippet(expr.span) 543 { 544 PathStatementDropSub::Suggestion { span: s.span, snippet } 545 } else { 546 PathStatementDropSub::Help { span: s.span } 547 }; 548 cx.emit_spanned_lint(PATH_STATEMENTS, s.span, PathStatementDrop { sub }) 549 } else { 550 cx.emit_spanned_lint(PATH_STATEMENTS, s.span, PathStatementNoEffect); 551 } 552 } 553 } 554 } 555 } 556 557 #[derive(Copy, Clone, Debug, PartialEq, Eq)] 558 enum UnusedDelimsCtx { 559 FunctionArg, 560 MethodArg, 561 AssignedValue, 562 AssignedValueLetElse, 563 IfCond, 564 WhileCond, 565 ForIterExpr, 566 MatchScrutineeExpr, 567 ReturnValue, 568 BlockRetValue, 569 LetScrutineeExpr, 570 ArrayLenExpr, 571 AnonConst, 572 MatchArmExpr, 573 IndexExpr, 574 } 575 576 impl From<UnusedDelimsCtx> for &'static str { from(ctx: UnusedDelimsCtx) -> &'static str577 fn from(ctx: UnusedDelimsCtx) -> &'static str { 578 match ctx { 579 UnusedDelimsCtx::FunctionArg => "function argument", 580 UnusedDelimsCtx::MethodArg => "method argument", 581 UnusedDelimsCtx::AssignedValue | UnusedDelimsCtx::AssignedValueLetElse => { 582 "assigned value" 583 } 584 UnusedDelimsCtx::IfCond => "`if` condition", 585 UnusedDelimsCtx::WhileCond => "`while` condition", 586 UnusedDelimsCtx::ForIterExpr => "`for` iterator expression", 587 UnusedDelimsCtx::MatchScrutineeExpr => "`match` scrutinee expression", 588 UnusedDelimsCtx::ReturnValue => "`return` value", 589 UnusedDelimsCtx::BlockRetValue => "block return value", 590 UnusedDelimsCtx::LetScrutineeExpr => "`let` scrutinee expression", 591 UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression", 592 UnusedDelimsCtx::MatchArmExpr => "match arm expression", 593 UnusedDelimsCtx::IndexExpr => "index expression", 594 } 595 } 596 } 597 598 /// Used by both `UnusedParens` and `UnusedBraces` to prevent code duplication. 599 trait UnusedDelimLint { 600 const DELIM_STR: &'static str; 601 602 /// Due to `ref` pattern, there can be a difference between using 603 /// `{ expr }` and `expr` in pattern-matching contexts. This means 604 /// that we should only lint `unused_parens` and not `unused_braces` 605 /// in this case. 606 /// 607 /// ```rust 608 /// let mut a = 7; 609 /// let ref b = { a }; // We actually borrow a copy of `a` here. 610 /// a += 1; // By mutating `a` we invalidate any borrows of `a`. 611 /// assert_eq!(b + 1, a); // `b` does not borrow `a`, so we can still use it here. 612 /// ``` 613 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool; 614 615 // this cannot be a constant is it refers to a static. lint(&self) -> &'static Lint616 fn lint(&self) -> &'static Lint; 617 check_unused_delims_expr( &self, cx: &EarlyContext<'_>, value: &ast::Expr, ctx: UnusedDelimsCtx, followed_by_block: bool, left_pos: Option<BytePos>, right_pos: Option<BytePos>, is_kw: bool, )618 fn check_unused_delims_expr( 619 &self, 620 cx: &EarlyContext<'_>, 621 value: &ast::Expr, 622 ctx: UnusedDelimsCtx, 623 followed_by_block: bool, 624 left_pos: Option<BytePos>, 625 right_pos: Option<BytePos>, 626 is_kw: bool, 627 ); 628 is_expr_delims_necessary( inner: &ast::Expr, followed_by_block: bool, followed_by_else: bool, ) -> bool629 fn is_expr_delims_necessary( 630 inner: &ast::Expr, 631 followed_by_block: bool, 632 followed_by_else: bool, 633 ) -> bool { 634 if followed_by_else { 635 match inner.kind { 636 ast::ExprKind::Binary(op, ..) if op.node.lazy() => return true, 637 _ if classify::expr_trailing_brace(inner).is_some() => return true, 638 _ => {} 639 } 640 } 641 642 // Check if LHS needs parens to prevent false-positives in cases like `fn x() -> u8 { ({ 0 } + 1) }`. 643 { 644 let mut innermost = inner; 645 loop { 646 innermost = match &innermost.kind { 647 ExprKind::Binary(_op, lhs, _rhs) => lhs, 648 ExprKind::Call(fn_, _params) => fn_, 649 ExprKind::Cast(expr, _ty) => expr, 650 ExprKind::Type(expr, _ty) => expr, 651 ExprKind::Index(base, _subscript) => base, 652 _ => break, 653 }; 654 if !classify::expr_requires_semi_to_be_stmt(innermost) { 655 return true; 656 } 657 } 658 } 659 660 // Check if RHS needs parens to prevent false-positives in cases like `if (() == return) {}`. 661 if !followed_by_block { 662 return false; 663 } 664 let mut innermost = inner; 665 loop { 666 innermost = match &innermost.kind { 667 ExprKind::Unary(_op, expr) => expr, 668 ExprKind::Binary(_op, _lhs, rhs) => rhs, 669 ExprKind::AssignOp(_op, _lhs, rhs) => rhs, 670 ExprKind::Assign(_lhs, rhs, _span) => rhs, 671 672 ExprKind::Ret(_) | ExprKind::Yield(..) | ExprKind::Yeet(..) => return true, 673 674 ExprKind::Break(_label, None) => return false, 675 ExprKind::Break(_label, Some(break_expr)) => { 676 return matches!(break_expr.kind, ExprKind::Block(..)); 677 } 678 679 ExprKind::Range(_lhs, Some(rhs), _limits) => { 680 return matches!(rhs.kind, ExprKind::Block(..)); 681 } 682 683 _ => return parser::contains_exterior_struct_lit(&inner), 684 } 685 } 686 } 687 emit_unused_delims_expr( &self, cx: &EarlyContext<'_>, value: &ast::Expr, ctx: UnusedDelimsCtx, left_pos: Option<BytePos>, right_pos: Option<BytePos>, is_kw: bool, )688 fn emit_unused_delims_expr( 689 &self, 690 cx: &EarlyContext<'_>, 691 value: &ast::Expr, 692 ctx: UnusedDelimsCtx, 693 left_pos: Option<BytePos>, 694 right_pos: Option<BytePos>, 695 is_kw: bool, 696 ) { 697 // If `value` has `ExprKind::Err`, unused delim lint can be broken. 698 // For example, the following code caused ICE. 699 // This is because the `ExprKind::Call` in `value` has `ExprKind::Err` as its argument 700 // and this leads to wrong spans. #104897 701 // 702 // ``` 703 // fn f(){(print!(á 704 // ``` 705 use rustc_ast::visit::{walk_expr, Visitor}; 706 struct ErrExprVisitor { 707 has_error: bool, 708 } 709 impl<'ast> Visitor<'ast> for ErrExprVisitor { 710 fn visit_expr(&mut self, expr: &'ast ast::Expr) { 711 if let ExprKind::Err = expr.kind { 712 self.has_error = true; 713 return; 714 } 715 walk_expr(self, expr) 716 } 717 } 718 let mut visitor = ErrExprVisitor { has_error: false }; 719 visitor.visit_expr(value); 720 if visitor.has_error { 721 return; 722 } 723 let spans = match value.kind { 724 ast::ExprKind::Block(ref block, None) if block.stmts.len() == 1 => block.stmts[0] 725 .span 726 .find_ancestor_inside(value.span) 727 .map(|span| (value.span.with_hi(span.lo()), value.span.with_lo(span.hi()))), 728 ast::ExprKind::Paren(ref expr) => { 729 expr.span.find_ancestor_inside(value.span).map(|expr_span| { 730 (value.span.with_hi(expr_span.lo()), value.span.with_lo(expr_span.hi())) 731 }) 732 } 733 _ => return, 734 }; 735 let keep_space = ( 736 left_pos.is_some_and(|s| s >= value.span.lo()), 737 right_pos.is_some_and(|s| s <= value.span.hi()), 738 ); 739 self.emit_unused_delims(cx, value.span, spans, ctx.into(), keep_space, is_kw); 740 } 741 emit_unused_delims( &self, cx: &EarlyContext<'_>, value_span: Span, spans: Option<(Span, Span)>, msg: &str, keep_space: (bool, bool), is_kw: bool, )742 fn emit_unused_delims( 743 &self, 744 cx: &EarlyContext<'_>, 745 value_span: Span, 746 spans: Option<(Span, Span)>, 747 msg: &str, 748 keep_space: (bool, bool), 749 is_kw: bool, 750 ) { 751 let primary_span = if let Some((lo, hi)) = spans { 752 if hi.is_empty() { 753 // do not point at delims that do not exist 754 return; 755 } 756 MultiSpan::from(vec![lo, hi]) 757 } else { 758 MultiSpan::from(value_span) 759 }; 760 let suggestion = spans.map(|(lo, hi)| { 761 let sm = cx.sess().source_map(); 762 let lo_replace = 763 if (keep_space.0 || is_kw) && 764 let Ok(snip) = sm.span_to_prev_source(lo) && !snip.ends_with(' ') { 765 " " 766 } else { 767 "" 768 }; 769 770 let hi_replace = 771 if keep_space.1 && 772 let Ok(snip) = sm.span_to_next_source(hi) && !snip.starts_with(' ') { 773 " " 774 } else { 775 "" 776 }; 777 UnusedDelimSuggestion { 778 start_span: lo, 779 start_replace: lo_replace, 780 end_span: hi, 781 end_replace: hi_replace, 782 } 783 }); 784 cx.emit_spanned_lint( 785 self.lint(), 786 primary_span, 787 UnusedDelim { delim: Self::DELIM_STR, item: msg, suggestion }, 788 ); 789 } 790 check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr)791 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { 792 use rustc_ast::ExprKind::*; 793 let (value, ctx, followed_by_block, left_pos, right_pos, is_kw) = match e.kind { 794 // Do not lint `unused_braces` in `if let` expressions. 795 If(ref cond, ref block, _) 796 if !matches!(cond.kind, Let(_, _, _)) 797 || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => 798 { 799 let left = e.span.lo() + rustc_span::BytePos(2); 800 let right = block.span.lo(); 801 (cond, UnusedDelimsCtx::IfCond, true, Some(left), Some(right), true) 802 } 803 804 // Do not lint `unused_braces` in `while let` expressions. 805 While(ref cond, ref block, ..) 806 if !matches!(cond.kind, Let(_, _, _)) 807 || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => 808 { 809 let left = e.span.lo() + rustc_span::BytePos(5); 810 let right = block.span.lo(); 811 (cond, UnusedDelimsCtx::WhileCond, true, Some(left), Some(right), true) 812 } 813 814 ForLoop(_, ref cond, ref block, ..) => { 815 (cond, UnusedDelimsCtx::ForIterExpr, true, None, Some(block.span.lo()), true) 816 } 817 818 Match(ref head, _) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { 819 let left = e.span.lo() + rustc_span::BytePos(5); 820 (head, UnusedDelimsCtx::MatchScrutineeExpr, true, Some(left), None, true) 821 } 822 823 Ret(Some(ref value)) => { 824 let left = e.span.lo() + rustc_span::BytePos(3); 825 (value, UnusedDelimsCtx::ReturnValue, false, Some(left), None, true) 826 } 827 828 Index(_, ref value) => (value, UnusedDelimsCtx::IndexExpr, false, None, None, false), 829 830 Assign(_, ref value, _) | AssignOp(.., ref value) => { 831 (value, UnusedDelimsCtx::AssignedValue, false, None, None, false) 832 } 833 // either function/method call, or something this lint doesn't care about 834 ref call_or_other => { 835 let (args_to_check, ctx) = match *call_or_other { 836 Call(_, ref args) => (&args[..], UnusedDelimsCtx::FunctionArg), 837 MethodCall(ref call) => (&call.args[..], UnusedDelimsCtx::MethodArg), 838 // actual catch-all arm 839 _ => { 840 return; 841 } 842 }; 843 // Don't lint if this is a nested macro expansion: otherwise, the lint could 844 // trigger in situations that macro authors shouldn't have to care about, e.g., 845 // when a parenthesized token tree matched in one macro expansion is matched as 846 // an expression in another and used as a fn/method argument (Issue #47775) 847 if e.span.ctxt().outer_expn_data().call_site.from_expansion() { 848 return; 849 } 850 for arg in args_to_check { 851 self.check_unused_delims_expr(cx, arg, ctx, false, None, None, false); 852 } 853 return; 854 } 855 }; 856 self.check_unused_delims_expr( 857 cx, 858 &value, 859 ctx, 860 followed_by_block, 861 left_pos, 862 right_pos, 863 is_kw, 864 ); 865 } 866 check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt)867 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { 868 match s.kind { 869 StmtKind::Local(ref local) if Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX => { 870 if let Some((init, els)) = local.kind.init_else_opt() { 871 let ctx = match els { 872 None => UnusedDelimsCtx::AssignedValue, 873 Some(_) => UnusedDelimsCtx::AssignedValueLetElse, 874 }; 875 self.check_unused_delims_expr(cx, init, ctx, false, None, None, false); 876 } 877 } 878 StmtKind::Expr(ref expr) => { 879 self.check_unused_delims_expr( 880 cx, 881 &expr, 882 UnusedDelimsCtx::BlockRetValue, 883 false, 884 None, 885 None, 886 false, 887 ); 888 } 889 _ => {} 890 } 891 } 892 check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item)893 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { 894 use ast::ItemKind::*; 895 896 if let Const(box ast::ConstItem { expr: Some(expr), .. }) 897 | Static(box ast::StaticItem { expr: Some(expr), .. }) = &item.kind 898 { 899 self.check_unused_delims_expr( 900 cx, 901 expr, 902 UnusedDelimsCtx::AssignedValue, 903 false, 904 None, 905 None, 906 false, 907 ); 908 } 909 } 910 } 911 912 declare_lint! { 913 /// The `unused_parens` lint detects `if`, `match`, `while` and `return` 914 /// with parentheses; they do not need them. 915 /// 916 /// ### Examples 917 /// 918 /// ```rust 919 /// if(true) {} 920 /// ``` 921 /// 922 /// {{produces}} 923 /// 924 /// ### Explanation 925 /// 926 /// The parentheses are not needed, and should be removed. This is the 927 /// preferred style for writing these expressions. 928 pub(super) UNUSED_PARENS, 929 Warn, 930 "`if`, `match`, `while` and `return` do not need parentheses" 931 } 932 933 pub struct UnusedParens { 934 with_self_ty_parens: bool, 935 } 936 937 impl UnusedParens { new() -> Self938 pub fn new() -> Self { 939 Self { with_self_ty_parens: false } 940 } 941 } 942 943 impl_lint_pass!(UnusedParens => [UNUSED_PARENS]); 944 945 impl UnusedDelimLint for UnusedParens { 946 const DELIM_STR: &'static str = "parentheses"; 947 948 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = true; 949 lint(&self) -> &'static Lint950 fn lint(&self) -> &'static Lint { 951 UNUSED_PARENS 952 } 953 check_unused_delims_expr( &self, cx: &EarlyContext<'_>, value: &ast::Expr, ctx: UnusedDelimsCtx, followed_by_block: bool, left_pos: Option<BytePos>, right_pos: Option<BytePos>, is_kw: bool, )954 fn check_unused_delims_expr( 955 &self, 956 cx: &EarlyContext<'_>, 957 value: &ast::Expr, 958 ctx: UnusedDelimsCtx, 959 followed_by_block: bool, 960 left_pos: Option<BytePos>, 961 right_pos: Option<BytePos>, 962 is_kw: bool, 963 ) { 964 match value.kind { 965 ast::ExprKind::Paren(ref inner) => { 966 let followed_by_else = ctx == UnusedDelimsCtx::AssignedValueLetElse; 967 if !Self::is_expr_delims_necessary(inner, followed_by_block, followed_by_else) 968 && value.attrs.is_empty() 969 && !value.span.from_expansion() 970 && (ctx != UnusedDelimsCtx::LetScrutineeExpr 971 || !matches!(inner.kind, ast::ExprKind::Binary( 972 rustc_span::source_map::Spanned { node, .. }, 973 _, 974 _, 975 ) if node.lazy())) 976 { 977 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw) 978 } 979 } 980 ast::ExprKind::Let(_, ref expr, _) => { 981 self.check_unused_delims_expr( 982 cx, 983 expr, 984 UnusedDelimsCtx::LetScrutineeExpr, 985 followed_by_block, 986 None, 987 None, 988 false, 989 ); 990 } 991 _ => {} 992 } 993 } 994 } 995 996 impl UnusedParens { check_unused_parens_pat( &self, cx: &EarlyContext<'_>, value: &ast::Pat, avoid_or: bool, avoid_mut: bool, keep_space: (bool, bool), )997 fn check_unused_parens_pat( 998 &self, 999 cx: &EarlyContext<'_>, 1000 value: &ast::Pat, 1001 avoid_or: bool, 1002 avoid_mut: bool, 1003 keep_space: (bool, bool), 1004 ) { 1005 use ast::{BindingAnnotation, PatKind}; 1006 1007 if let PatKind::Paren(inner) = &value.kind { 1008 match inner.kind { 1009 // The lint visitor will visit each subpattern of `p`. We do not want to lint 1010 // any range pattern no matter where it occurs in the pattern. For something like 1011 // `&(a..=b)`, there is a recursive `check_pat` on `a` and `b`, but we will assume 1012 // that if there are unnecessary parens they serve a purpose of readability. 1013 PatKind::Range(..) => return, 1014 // Avoid `p0 | .. | pn` if we should. 1015 PatKind::Or(..) if avoid_or => return, 1016 // Avoid `mut x` and `mut x @ p` if we should: 1017 PatKind::Ident(BindingAnnotation::MUT, ..) if avoid_mut => { 1018 return; 1019 } 1020 // Otherwise proceed with linting. 1021 _ => {} 1022 } 1023 let spans = inner 1024 .span 1025 .find_ancestor_inside(value.span) 1026 .map(|inner| (value.span.with_hi(inner.lo()), value.span.with_lo(inner.hi()))); 1027 self.emit_unused_delims(cx, value.span, spans, "pattern", keep_space, false); 1028 } 1029 } 1030 } 1031 1032 impl EarlyLintPass for UnusedParens { 1033 #[inline] check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr)1034 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { 1035 match e.kind { 1036 ExprKind::Let(ref pat, _, _) | ExprKind::ForLoop(ref pat, ..) => { 1037 self.check_unused_parens_pat(cx, pat, false, false, (true, true)); 1038 } 1039 // We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already 1040 // handle a hard error for them during AST lowering in `lower_expr_mut`, but we still 1041 // want to complain about things like `if let 42 = (42)`. 1042 ExprKind::If(ref cond, ref block, ref else_) 1043 if matches!(cond.peel_parens().kind, ExprKind::Let(..)) => 1044 { 1045 self.check_unused_delims_expr( 1046 cx, 1047 cond.peel_parens(), 1048 UnusedDelimsCtx::LetScrutineeExpr, 1049 true, 1050 None, 1051 None, 1052 true, 1053 ); 1054 for stmt in &block.stmts { 1055 <Self as UnusedDelimLint>::check_stmt(self, cx, stmt); 1056 } 1057 if let Some(e) = else_ { 1058 <Self as UnusedDelimLint>::check_expr(self, cx, e); 1059 } 1060 return; 1061 } 1062 ExprKind::Match(ref _expr, ref arm) => { 1063 for a in arm { 1064 self.check_unused_delims_expr( 1065 cx, 1066 &a.body, 1067 UnusedDelimsCtx::MatchArmExpr, 1068 false, 1069 None, 1070 None, 1071 true, 1072 ); 1073 } 1074 } 1075 _ => {} 1076 } 1077 1078 <Self as UnusedDelimLint>::check_expr(self, cx, e) 1079 } 1080 check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat)1081 fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &ast::Pat) { 1082 use ast::{Mutability, PatKind::*}; 1083 let keep_space = (false, false); 1084 match &p.kind { 1085 // Do not lint on `(..)` as that will result in the other arms being useless. 1086 Paren(_) 1087 // The other cases do not contain sub-patterns. 1088 | Wild | Rest | Lit(..) | MacCall(..) | Range(..) | Ident(.., None) | Path(..) => {}, 1089 // These are list-like patterns; parens can always be removed. 1090 TupleStruct(_, _, ps) | Tuple(ps) | Slice(ps) | Or(ps) => for p in ps { 1091 self.check_unused_parens_pat(cx, p, false, false, keep_space); 1092 }, 1093 Struct(_, _, fps, _) => for f in fps { 1094 self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space); 1095 }, 1096 // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106. 1097 Ident(.., Some(p)) | Box(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space), 1098 // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342. 1099 // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106. 1100 Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space), 1101 } 1102 } 1103 check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt)1104 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { 1105 if let StmtKind::Local(ref local) = s.kind { 1106 self.check_unused_parens_pat(cx, &local.pat, true, false, (false, false)); 1107 } 1108 1109 <Self as UnusedDelimLint>::check_stmt(self, cx, s) 1110 } 1111 check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param)1112 fn check_param(&mut self, cx: &EarlyContext<'_>, param: &ast::Param) { 1113 self.check_unused_parens_pat(cx, ¶m.pat, true, false, (false, false)); 1114 } 1115 check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm)1116 fn check_arm(&mut self, cx: &EarlyContext<'_>, arm: &ast::Arm) { 1117 self.check_unused_parens_pat(cx, &arm.pat, false, false, (false, false)); 1118 } 1119 check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty)1120 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { 1121 match &ty.kind { 1122 ast::TyKind::Array(_, len) => { 1123 self.check_unused_delims_expr( 1124 cx, 1125 &len.value, 1126 UnusedDelimsCtx::ArrayLenExpr, 1127 false, 1128 None, 1129 None, 1130 false, 1131 ); 1132 } 1133 ast::TyKind::Paren(r) => { 1134 match &r.kind { 1135 ast::TyKind::TraitObject(..) => {} 1136 ast::TyKind::BareFn(b) 1137 if self.with_self_ty_parens && b.generic_params.len() > 0 => {} 1138 ast::TyKind::ImplTrait(_, bounds) if bounds.len() > 1 => {} 1139 _ => { 1140 let spans = r 1141 .span 1142 .find_ancestor_inside(ty.span) 1143 .map(|r| (ty.span.with_hi(r.lo()), ty.span.with_lo(r.hi()))); 1144 1145 self.emit_unused_delims(cx, ty.span, spans, "type", (false, false), false); 1146 } 1147 } 1148 self.with_self_ty_parens = false; 1149 } 1150 _ => {} 1151 } 1152 } 1153 check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item)1154 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { 1155 <Self as UnusedDelimLint>::check_item(self, cx, item) 1156 } 1157 enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate)1158 fn enter_where_predicate(&mut self, _: &EarlyContext<'_>, pred: &ast::WherePredicate) { 1159 use rustc_ast::{WhereBoundPredicate, WherePredicate}; 1160 if let WherePredicate::BoundPredicate(WhereBoundPredicate { 1161 bounded_ty, 1162 bound_generic_params, 1163 .. 1164 }) = pred && 1165 let ast::TyKind::Paren(_) = &bounded_ty.kind && 1166 bound_generic_params.is_empty() { 1167 self.with_self_ty_parens = true; 1168 } 1169 } 1170 exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate)1171 fn exit_where_predicate(&mut self, _: &EarlyContext<'_>, _: &ast::WherePredicate) { 1172 assert!(!self.with_self_ty_parens); 1173 } 1174 } 1175 1176 declare_lint! { 1177 /// The `unused_braces` lint detects unnecessary braces around an 1178 /// expression. 1179 /// 1180 /// ### Example 1181 /// 1182 /// ```rust 1183 /// if { true } { 1184 /// // ... 1185 /// } 1186 /// ``` 1187 /// 1188 /// {{produces}} 1189 /// 1190 /// ### Explanation 1191 /// 1192 /// The braces are not needed, and should be removed. This is the 1193 /// preferred style for writing these expressions. 1194 pub(super) UNUSED_BRACES, 1195 Warn, 1196 "unnecessary braces around an expression" 1197 } 1198 1199 declare_lint_pass!(UnusedBraces => [UNUSED_BRACES]); 1200 1201 impl UnusedDelimLint for UnusedBraces { 1202 const DELIM_STR: &'static str = "braces"; 1203 1204 const LINT_EXPR_IN_PATTERN_MATCHING_CTX: bool = false; 1205 lint(&self) -> &'static Lint1206 fn lint(&self) -> &'static Lint { 1207 UNUSED_BRACES 1208 } 1209 check_unused_delims_expr( &self, cx: &EarlyContext<'_>, value: &ast::Expr, ctx: UnusedDelimsCtx, followed_by_block: bool, left_pos: Option<BytePos>, right_pos: Option<BytePos>, is_kw: bool, )1210 fn check_unused_delims_expr( 1211 &self, 1212 cx: &EarlyContext<'_>, 1213 value: &ast::Expr, 1214 ctx: UnusedDelimsCtx, 1215 followed_by_block: bool, 1216 left_pos: Option<BytePos>, 1217 right_pos: Option<BytePos>, 1218 is_kw: bool, 1219 ) { 1220 match value.kind { 1221 ast::ExprKind::Block(ref inner, None) 1222 if inner.rules == ast::BlockCheckMode::Default => 1223 { 1224 // emit a warning under the following conditions: 1225 // 1226 // - the block does not have a label 1227 // - the block is not `unsafe` 1228 // - the block contains exactly one expression (do not lint `{ expr; }`) 1229 // - `followed_by_block` is true and the internal expr may contain a `{` 1230 // - the block is not multiline (do not lint multiline match arms) 1231 // ``` 1232 // match expr { 1233 // Pattern => { 1234 // somewhat_long_expression 1235 // } 1236 // // ... 1237 // } 1238 // ``` 1239 // - the block has no attribute and was not created inside a macro 1240 // - if the block is an `anon_const`, the inner expr must be a literal 1241 // not created by a macro, i.e. do not lint on: 1242 // ``` 1243 // struct A<const N: usize>; 1244 // let _: A<{ 2 + 3 }>; 1245 // let _: A<{produces_literal!()}>; 1246 // ``` 1247 // FIXME(const_generics): handle paths when #67075 is fixed. 1248 if let [stmt] = inner.stmts.as_slice() { 1249 if let ast::StmtKind::Expr(ref expr) = stmt.kind { 1250 if !Self::is_expr_delims_necessary(expr, followed_by_block, false) 1251 && (ctx != UnusedDelimsCtx::AnonConst 1252 || (matches!(expr.kind, ast::ExprKind::Lit(_)) 1253 && !expr.span.from_expansion())) 1254 && !cx.sess().source_map().is_multiline(value.span) 1255 && value.attrs.is_empty() 1256 && !value.span.from_expansion() 1257 && !inner.span.from_expansion() 1258 { 1259 self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos, is_kw) 1260 } 1261 } 1262 } 1263 } 1264 ast::ExprKind::Let(_, ref expr, _) => { 1265 self.check_unused_delims_expr( 1266 cx, 1267 expr, 1268 UnusedDelimsCtx::LetScrutineeExpr, 1269 followed_by_block, 1270 None, 1271 None, 1272 false, 1273 ); 1274 } 1275 _ => {} 1276 } 1277 } 1278 } 1279 1280 impl EarlyLintPass for UnusedBraces { check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt)1281 fn check_stmt(&mut self, cx: &EarlyContext<'_>, s: &ast::Stmt) { 1282 <Self as UnusedDelimLint>::check_stmt(self, cx, s) 1283 } 1284 1285 #[inline] check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr)1286 fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) { 1287 <Self as UnusedDelimLint>::check_expr(self, cx, e); 1288 1289 if let ExprKind::Repeat(_, ref anon_const) = e.kind { 1290 self.check_unused_delims_expr( 1291 cx, 1292 &anon_const.value, 1293 UnusedDelimsCtx::AnonConst, 1294 false, 1295 None, 1296 None, 1297 false, 1298 ); 1299 } 1300 } 1301 check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg)1302 fn check_generic_arg(&mut self, cx: &EarlyContext<'_>, arg: &ast::GenericArg) { 1303 if let ast::GenericArg::Const(ct) = arg { 1304 self.check_unused_delims_expr( 1305 cx, 1306 &ct.value, 1307 UnusedDelimsCtx::AnonConst, 1308 false, 1309 None, 1310 None, 1311 false, 1312 ); 1313 } 1314 } 1315 check_variant(&mut self, cx: &EarlyContext<'_>, v: &ast::Variant)1316 fn check_variant(&mut self, cx: &EarlyContext<'_>, v: &ast::Variant) { 1317 if let Some(anon_const) = &v.disr_expr { 1318 self.check_unused_delims_expr( 1319 cx, 1320 &anon_const.value, 1321 UnusedDelimsCtx::AnonConst, 1322 false, 1323 None, 1324 None, 1325 false, 1326 ); 1327 } 1328 } 1329 check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty)1330 fn check_ty(&mut self, cx: &EarlyContext<'_>, ty: &ast::Ty) { 1331 match ty.kind { 1332 ast::TyKind::Array(_, ref len) => { 1333 self.check_unused_delims_expr( 1334 cx, 1335 &len.value, 1336 UnusedDelimsCtx::ArrayLenExpr, 1337 false, 1338 None, 1339 None, 1340 false, 1341 ); 1342 } 1343 1344 ast::TyKind::Typeof(ref anon_const) => { 1345 self.check_unused_delims_expr( 1346 cx, 1347 &anon_const.value, 1348 UnusedDelimsCtx::AnonConst, 1349 false, 1350 None, 1351 None, 1352 false, 1353 ); 1354 } 1355 1356 _ => {} 1357 } 1358 } 1359 check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item)1360 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { 1361 <Self as UnusedDelimLint>::check_item(self, cx, item) 1362 } 1363 } 1364 1365 declare_lint! { 1366 /// The `unused_import_braces` lint catches unnecessary braces around an 1367 /// imported item. 1368 /// 1369 /// ### Example 1370 /// 1371 /// ```rust,compile_fail 1372 /// #![deny(unused_import_braces)] 1373 /// use test::{A}; 1374 /// 1375 /// pub mod test { 1376 /// pub struct A; 1377 /// } 1378 /// # fn main() {} 1379 /// ``` 1380 /// 1381 /// {{produces}} 1382 /// 1383 /// ### Explanation 1384 /// 1385 /// If there is only a single item, then remove the braces (`use test::A;` 1386 /// for example). 1387 /// 1388 /// This lint is "allow" by default because it is only enforcing a 1389 /// stylistic choice. 1390 UNUSED_IMPORT_BRACES, 1391 Allow, 1392 "unnecessary braces around an imported item" 1393 } 1394 1395 declare_lint_pass!(UnusedImportBraces => [UNUSED_IMPORT_BRACES]); 1396 1397 impl UnusedImportBraces { check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item)1398 fn check_use_tree(&self, cx: &EarlyContext<'_>, use_tree: &ast::UseTree, item: &ast::Item) { 1399 if let ast::UseTreeKind::Nested(ref items) = use_tree.kind { 1400 // Recursively check nested UseTrees 1401 for (tree, _) in items { 1402 self.check_use_tree(cx, tree, item); 1403 } 1404 1405 // Trigger the lint only if there is one nested item 1406 if items.len() != 1 { 1407 return; 1408 } 1409 1410 // Trigger the lint if the nested item is a non-self single item 1411 let node_name = match items[0].0.kind { 1412 ast::UseTreeKind::Simple(rename) => { 1413 let orig_ident = items[0].0.prefix.segments.last().unwrap().ident; 1414 if orig_ident.name == kw::SelfLower { 1415 return; 1416 } 1417 rename.unwrap_or(orig_ident).name 1418 } 1419 ast::UseTreeKind::Glob => Symbol::intern("*"), 1420 ast::UseTreeKind::Nested(_) => return, 1421 }; 1422 1423 cx.emit_spanned_lint( 1424 UNUSED_IMPORT_BRACES, 1425 item.span, 1426 UnusedImportBracesDiag { node: node_name }, 1427 ); 1428 } 1429 } 1430 } 1431 1432 impl EarlyLintPass for UnusedImportBraces { check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item)1433 fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { 1434 if let ast::ItemKind::Use(ref use_tree) = item.kind { 1435 self.check_use_tree(cx, use_tree, item); 1436 } 1437 } 1438 } 1439 1440 declare_lint! { 1441 /// The `unused_allocation` lint detects unnecessary allocations that can 1442 /// be eliminated. 1443 /// 1444 /// ### Example 1445 /// 1446 /// ```rust 1447 /// fn main() { 1448 /// let a = Box::new([1, 2, 3]).len(); 1449 /// } 1450 /// ``` 1451 /// 1452 /// {{produces}} 1453 /// 1454 /// ### Explanation 1455 /// 1456 /// When a `box` expression is immediately coerced to a reference, then 1457 /// the allocation is unnecessary, and a reference (using `&` or `&mut`) 1458 /// should be used instead to avoid the allocation. 1459 pub(super) UNUSED_ALLOCATION, 1460 Warn, 1461 "detects unnecessary allocations that can be eliminated" 1462 } 1463 1464 declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]); 1465 1466 impl<'tcx> LateLintPass<'tcx> for UnusedAllocation { check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>)1467 fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) { 1468 match e.kind { 1469 hir::ExprKind::Call(path_expr, [_]) 1470 if let hir::ExprKind::Path(qpath) = &path_expr.kind 1471 && let Some(did) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id() 1472 && cx.tcx.is_diagnostic_item(sym::box_new, did) 1473 => {} 1474 _ => return, 1475 } 1476 1477 for adj in cx.typeck_results().expr_adjustments(e) { 1478 if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(_, m)) = adj.kind { 1479 match m { 1480 adjustment::AutoBorrowMutability::Not => { 1481 cx.emit_spanned_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag); 1482 } 1483 adjustment::AutoBorrowMutability::Mut { .. } => { 1484 cx.emit_spanned_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationMutDiag); 1485 } 1486 }; 1487 } 1488 } 1489 } 1490 } 1491