|
3 | 3 | #![allow(clippy::module_name_repetitions)] |
4 | 4 |
|
5 | 5 | use core::ops::ControlFlow; |
| 6 | +use itertools::Itertools as _; |
6 | 7 | use rustc_abi::{BackendRepr, FieldsShape, VariantIdx, Variants}; |
7 | 8 | use rustc_ast::ast::Mutability; |
8 | 9 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; |
| 10 | +use rustc_errors::pluralize; |
9 | 11 | use rustc_hir as hir; |
10 | 12 | use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; |
11 | 13 | use rustc_hir::def_id::DefId; |
12 | | -use rustc_hir::{Expr, FnDecl, LangItem, find_attr}; |
| 14 | +use rustc_hir::{Expr, ExprKind, FnDecl, LangItem}; |
13 | 15 | use rustc_hir_analysis::lower_ty; |
14 | 16 | use rustc_infer::infer::TyCtxtInferExt as _; |
15 | 17 | use rustc_lint::LateContext; |
| 18 | +use rustc_lint::unused::must_use::{IsTyMustUse, MustUsePath, is_ty_must_use}; |
16 | 19 | use rustc_middle::mir::ConstValue; |
17 | 20 | use rustc_middle::mir::interpret::Scalar; |
18 | 21 | use rustc_middle::traits::EvaluationResult; |
@@ -319,54 +322,112 @@ pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { |
319 | 322 | } |
320 | 323 | } |
321 | 324 |
|
322 | | -// Returns whether the `ty` has `#[must_use]` attribute. If `ty` is a `Result`/`ControlFlow` |
323 | | -// whose `Err`/`Break` payload is an uninhabited type, the `Ok`/`Continue` payload type |
324 | | -// will be used instead. See <https://github.com/rust-lang/rust/pull/148214>. |
325 | | -pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { |
326 | | - match ty.kind() { |
327 | | - ty::Adt(adt, args) => match cx.tcx.get_diagnostic_name(adt.did()) { |
328 | | - Some(sym::Result) if args.type_at(1).is_privately_uninhabited(cx.tcx, cx.typing_env()) => { |
329 | | - is_must_use_ty(cx, args.type_at(0)) |
330 | | - }, |
331 | | - Some(sym::ControlFlow) if args.type_at(0).is_privately_uninhabited(cx.tcx, cx.typing_env()) => { |
332 | | - is_must_use_ty(cx, args.type_at(1)) |
333 | | - }, |
334 | | - _ => find_attr!(cx.tcx, adt.did(), MustUse { .. }), |
| 325 | +/// Returns whether the `ty` has `#[must_use]` attribute, or acts like it does according to the |
| 326 | +/// compiler determination. For example, if `ty` is a `Result`/`ControlFlow` whose `Err`/`Break` |
| 327 | +/// payload is an uninhabited type, the `Ok`/`Continue` payload type will be used instead. |
| 328 | +/// |
| 329 | +/// The [`MustUsePath`] can be used to describe the type through [`describe_must_use_type`]. |
| 330 | +pub fn opt_must_use_path<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<MustUsePath> { |
| 331 | + // `is_ty_must_use` requires an expression, whose `hir_id` will be used to determine whether |
| 332 | + // certain types are visibly uninhabited from the module containing the expression. |
| 333 | + // `cx.last_node_with_lint_attrs` is initialized to the crate/module `hir_id` when linting |
| 334 | + // a new crate/module. If it is overriden, it is with an `hir_id` pertaining to the same |
| 335 | + // create/module. We can use this in a dummy expression instead of asking all callers |
| 336 | + // to provide a local `hir_id` which would not add more information. |
| 337 | + let dummy_expr = Expr { |
| 338 | + hir_id: cx.last_node_with_lint_attrs, |
| 339 | + span: DUMMY_SP, |
| 340 | + kind: ExprKind::Ret(None), |
| 341 | + }; |
| 342 | + match is_ty_must_use(cx, ty, &dummy_expr) { |
| 343 | + IsTyMustUse::Yes(path) => Some(path), |
| 344 | + _ => None, |
| 345 | + } |
| 346 | +} |
| 347 | + |
| 348 | +/// Describe a [`MustUsePath`] returned by [`is_must_use_ty`]. |
| 349 | +pub fn describe_must_use_type(cx: &LateContext<'_>, path: &MustUsePath) -> String { |
| 350 | + describe_must_use_type_inner(cx, path, "", "", 1) |
| 351 | +} |
| 352 | + |
| 353 | +// This is a rip-off from the compiler's `rustc_lint/src/unused/must_use.rs` |
| 354 | +fn describe_must_use_type_inner( |
| 355 | + cx: &LateContext<'_>, |
| 356 | + path: &MustUsePath, |
| 357 | + descr_pre: &str, |
| 358 | + descr_post: &str, |
| 359 | + plural_len: usize, |
| 360 | +) -> String { |
| 361 | + let plural_suffix = pluralize!(plural_len); |
| 362 | + |
| 363 | + match path { |
| 364 | + MustUsePath::Boxed(path) => { |
| 365 | + let descr_pre = &format!("{descr_pre}boxed "); |
| 366 | + describe_must_use_type_inner(cx, path, descr_pre, descr_post, plural_len) |
335 | 367 | }, |
336 | | - ty::Foreign(did) => find_attr!(cx.tcx, *did, MustUse { .. }), |
337 | | - ty::Slice(ty) | ty::Array(ty, _) | ty::RawPtr(ty, _) | ty::Ref(_, ty, _) => { |
338 | | - // for the Array case we don't need to care for the len == 0 case |
339 | | - // because we don't want to lint functions returning empty arrays |
340 | | - is_must_use_ty(cx, *ty) |
| 368 | + MustUsePath::Pinned(path) => { |
| 369 | + let descr_pre = &format!("{descr_pre}pinned "); |
| 370 | + describe_must_use_type_inner(cx, path, descr_pre, descr_post, plural_len) |
341 | 371 | }, |
342 | | - ty::Tuple(args) => args.iter().any(|ty| is_must_use_ty(cx, ty)), |
343 | | - ty::Alias( |
344 | | - _, |
345 | | - AliasTy { |
346 | | - kind: ty::Opaque { def_id }, |
347 | | - .. |
348 | | - }, |
349 | | - ) => { |
350 | | - for (predicate, _) in cx.tcx.explicit_item_self_bounds(*def_id).skip_binder() { |
351 | | - if let ty::ClauseKind::Trait(trait_predicate) = predicate.kind().skip_binder() |
352 | | - && find_attr!(cx.tcx, trait_predicate.trait_ref.def_id, MustUse { .. }) |
353 | | - { |
354 | | - return true; |
| 372 | + MustUsePath::Opaque(path) => { |
| 373 | + let descr_pre = &format!("{descr_pre}implementer{plural_suffix} of "); |
| 374 | + describe_must_use_type_inner(cx, path, descr_pre, descr_post, plural_len) |
| 375 | + }, |
| 376 | + MustUsePath::TraitObject(path) => { |
| 377 | + let descr_post = &format!(" trait object{plural_suffix}{descr_post}"); |
| 378 | + describe_must_use_type_inner(cx, path, descr_pre, descr_post, plural_len) |
| 379 | + }, |
| 380 | + MustUsePath::TupleElement(elems) => elems |
| 381 | + .iter() |
| 382 | + .map(|(index, path)| { |
| 383 | + let descr_post = &format!(" in tuple element {index}"); |
| 384 | + describe_must_use_type_inner(cx, path, descr_pre, descr_post, plural_len) |
| 385 | + }) |
| 386 | + .join(", "), |
| 387 | + MustUsePath::Result(path) => { |
| 388 | + let descr_post = &format!(" in a `Result` with an uninhabited error{descr_post}"); |
| 389 | + describe_must_use_type_inner(cx, path, descr_pre, descr_post, plural_len) |
| 390 | + }, |
| 391 | + MustUsePath::ControlFlow(path) => { |
| 392 | + let descr_post = &format!(" in a `ControlFlow` with an uninhabited break{descr_post}"); |
| 393 | + describe_must_use_type_inner(cx, path, descr_pre, descr_post, plural_len) |
| 394 | + }, |
| 395 | + MustUsePath::Array(path, len) => { |
| 396 | + let descr_pre = &format!("{descr_pre}array{plural_suffix} of "); |
| 397 | + describe_must_use_type_inner( |
| 398 | + cx, |
| 399 | + path, |
| 400 | + descr_pre, |
| 401 | + descr_post, |
| 402 | + plural_len.saturating_add(usize::try_from(*len).unwrap_or(usize::MAX)), |
| 403 | + ) |
| 404 | + }, |
| 405 | + MustUsePath::Closure(_) => { |
| 406 | + format!( |
| 407 | + "{descr_pre}{} closure{plural_suffix}{descr_post}", |
| 408 | + if plural_len == 1 { |
| 409 | + "one".to_string() |
| 410 | + } else { |
| 411 | + plural_len.to_string() |
355 | 412 | } |
356 | | - } |
357 | | - false |
| 413 | + ) |
358 | 414 | }, |
359 | | - ty::Dynamic(binder, _) => { |
360 | | - for predicate in *binder { |
361 | | - if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() |
362 | | - && find_attr!(cx.tcx, trait_ref.def_id, MustUse { .. }) |
363 | | - { |
364 | | - return true; |
| 415 | + MustUsePath::Coroutine(_) => { |
| 416 | + format!( |
| 417 | + "{descr_pre}{} coroutine{plural_suffix}{descr_post}", |
| 418 | + if plural_len == 1 { |
| 419 | + "one".to_string() |
| 420 | + } else { |
| 421 | + plural_len.to_string() |
365 | 422 | } |
366 | | - } |
367 | | - false |
| 423 | + ) |
| 424 | + }, |
| 425 | + MustUsePath::Def(_, def_id, _) => { |
| 426 | + format!( |
| 427 | + "{descr_pre}`{}`{plural_suffix}{descr_post}", |
| 428 | + cx.tcx.def_path_str(*def_id) |
| 429 | + ) |
368 | 430 | }, |
369 | | - _ => false, |
370 | 431 | } |
371 | 432 | } |
372 | 433 |
|
|
0 commit comments