Merge commit 'c9139bd546d9cd69df817faeab62c5f9b1a51337' into clippy-subtree-update

This commit is contained in:
Philipp Krones 2024-05-30 10:49:05 +02:00
commit 9b320af787
No known key found for this signature in database
GPG Key ID: 1CA0DF2AF59D68A5
128 changed files with 1430 additions and 684 deletions

View File

@ -133,7 +133,7 @@ Very rarely, you may wish to prevent Clippy from evaluating certain sections of
`clippy` cfg is not set. You may need to provide a stub so that the code compiles:
```rust
#[cfg(not(clippy)]
#[cfg(not(clippy))]
include!(concat!(env!("OUT_DIR"), "/my_big_function-generated.rs"));
#[cfg(clippy)]

View File

@ -587,6 +587,11 @@ declare_clippy_lint! {
}
```
If the lint is in the `restriction` group because it lints things that are not
necessarily “bad” but are more of a style choice, then replace the
“Why is this bad?” section heading with “Why restrict this?”, to avoid writing
“Why is this bad? It isn't, but ...”.
Once your lint is merged, this documentation will show up in the [lint
list][lint_list].

View File

@ -331,12 +331,17 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
}
fn get_lint_declaration(name_upper: &str, category: &str) -> String {
let justification_heading = if category == "restriction" {
"Why restrict this?"
} else {
"Why is this bad?"
};
formatdoc!(
r#"
declare_clippy_lint! {{
/// ### What it does
///
/// ### Why is this bad?
/// ### {justification_heading}
///
/// ### Example
/// ```no_run

View File

@ -12,7 +12,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for usage of items through absolute paths, like `std::env::current_dir`.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Many codebases have their own style when it comes to importing, but one that is seldom used
/// is using absolute paths *everywhere*. This is generally considered unidiomatic, and you
/// should add a `use` statement.

View File

@ -19,10 +19,11 @@ declare_clippy_lint! {
/// This lint only warns outer attributes (`#[allow]`), as inner attributes
/// (`#![allow]`) are usually used to enable or disable lints on a global scale.
///
/// ### Why is this bad?
/// `#[expect]` attributes suppress the lint emission, but emit a warning, if
/// ### Why restrict this?
/// `#[allow]` attributes can linger after their reason for existence is gone.
/// `#[expect]` attributes suppress the lint emission, but emit a warning if
/// the expectation is unfulfilled. This can be useful to be notified when the
/// lint is no longer triggered.
/// lint is no longer triggered, which may indicate the attribute can be removed.
///
/// ### Example
/// ```rust,ignore

View File

@ -17,7 +17,7 @@ declare_clippy_lint! {
/// There is a good explanation the reason why this lint should work in this way and how it is useful
/// [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122).
///
/// ### Why is this bad?
/// ### Why restrict this?
/// `as` conversions will perform many kinds of
/// conversions, including silently lossy conversions and dangerous coercions.
/// There are cases when it makes sense to use `as`, so the lint is

View File

@ -65,9 +65,8 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for usage of Intel x86 assembly syntax.
///
/// ### Why is this bad?
/// The lint has been enabled to indicate a preference
/// for AT&T x86 assembly syntax.
/// ### Why restrict this?
/// To enforce consistent use of AT&T x86 assembly syntax.
///
/// ### Example
///
@ -114,9 +113,8 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for usage of AT&T x86 assembly syntax.
///
/// ### Why is this bad?
/// The lint has been enabled to indicate a preference
/// for Intel x86 assembly syntax.
/// ### Why restrict this?
/// To enforce consistent use of Intel x86 assembly syntax.
///
/// ### Example
///

View File

@ -16,23 +16,33 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for `assert!(r.is_ok())` or `assert!(r.is_err())` calls.
///
/// ### Why is this bad?
/// An assertion failure cannot output an useful message of the error.
/// ### Why restrict this?
/// This form of assertion does not show any of the information present in the `Result`
/// other than which variant it isnt.
///
/// ### Known problems
/// The suggested replacement decreases the readability of code and log output.
///
/// ### Example
/// ```rust,ignore
/// ```rust,no_run
/// # let r = Ok::<_, ()>(());
/// assert!(r.is_ok());
/// # let r = Err::<_, ()>(());
/// # let r = Err::<(), _>(());
/// assert!(r.is_err());
/// ```
///
/// Use instead:
///
/// ```rust,no_run
/// # let r = Ok::<_, ()>(());
/// r.unwrap();
/// # let r = Err::<(), _>(());
/// r.unwrap_err();
/// ```
#[clippy::version = "1.64.0"]
pub ASSERTIONS_ON_RESULT_STATES,
restriction,
"`assert!(r.is_ok())`/`assert!(r.is_err())` gives worse error message than directly calling `r.unwrap()`/`r.unwrap_err()`"
"`assert!(r.is_ok())` or `assert!(r.is_err())` gives worse panic messages than directly calling `r.unwrap()` or `r.unwrap_err()`"
}
declare_lint_pass!(AssertionsOnResultStates => [ASSERTIONS_ON_RESULT_STATES]);

View File

@ -309,9 +309,9 @@ declare_clippy_lint! {
///
/// (This requires the `lint_reasons` feature)
///
/// ### Why is this bad?
/// Allowing a lint should always have a reason. This reason should be documented to
/// ensure that others understand the reasoning
/// ### Why restrict this?
/// Justifying each `allow` helps readers understand the reasoning,
/// and may allow removing `allow` attributes if their purpose is obsolete.
///
/// ### Example
/// ```no_run

View File

@ -303,7 +303,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for casts of a function pointer to any integer type.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Casting a function pointer to an integer can have surprising results and can occur
/// accidentally if parentheses are omitted from a function call. If you aren't doing anything
/// low-level with function pointers then you can opt-out of casting functions to integers in
@ -535,8 +535,8 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for the usage of `as _` conversion using inferred type.
///
/// ### Why is this bad?
/// The conversion might include lossy conversion and dangerous cast that might go
/// ### Why restrict this?
/// The conversion might include lossy conversion or a dangerous cast that might go
/// undetected due to the type being inferred.
///
/// The lint is allowed by default as using `_` is less wordy than always specifying the type.

View File

@ -10,8 +10,10 @@ declare_clippy_lint! {
/// ### What it does
/// Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead.
///
/// ### Why is this bad?
/// Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`.
/// ### Why restrict this?
/// Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`,
/// resulting in failure when more than one directory needs to be created or when the directory already exists.
/// Crates which never need to specifically create a single directory may wish to prevent this mistake.
///
/// ### Example
/// ```rust,ignore

View File

@ -14,7 +14,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for usage of the [`dbg!`](https://doc.rust-lang.org/std/macro.dbg.html) macro.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// The `dbg!` macro is intended as a debugging tool. It should not be present in released
/// software or committed to a version control system.
///

View File

@ -22,9 +22,8 @@ declare_clippy_lint! {
///
/// See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback.
///
/// ### Why is this bad?
/// For those who are very careful about types, default numeric fallback
/// can be a pitfall that cause unexpected runtime behavior.
/// ### Why restrict this?
/// To ensure that every numeric type is chosen explicitly rather than implicitly.
///
/// ### Known problems
/// This lint can only be allowed at the function level or above.

View File

@ -10,7 +10,7 @@ declare_clippy_lint! {
/// ### What it does
/// Displays a warning when a union is declared with the default representation (without a `#[repr(C)]` attribute).
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Unions in Rust have unspecified layout by default, despite many people thinking that they
/// lay out each field at the start of the union (like C does). That is, there are no guarantees
/// about the offset of the fields for unions with multiple non-ZST fields without an explicitly

View File

@ -2,7 +2,9 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::ty::{implements_trait, is_manually_drop, peel_mid_ty_refs};
use clippy_utils::{expr_use_ctxt, get_parent_expr, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode};
use clippy_utils::{
expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode,
};
use core::mem;
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
use rustc_data_structures::fx::FxIndexMap;
@ -1038,14 +1040,8 @@ fn report<'tcx>(
);
},
State::ExplicitDeref { mutability } => {
if matches!(
expr.kind,
ExprKind::Block(..)
| ExprKind::ConstBlock(_)
| ExprKind::If(..)
| ExprKind::Loop(..)
| ExprKind::Match(..)
) && let ty::Ref(_, ty, _) = data.adjusted_ty.kind()
if is_block_like(expr)
&& let ty::Ref(_, ty, _) = data.adjusted_ty.kind()
&& ty.is_sized(cx.tcx, cx.param_env)
{
// Rustc bug: auto deref doesn't work on block expression when targeting sized types.

View File

@ -1,17 +1,17 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then, span_lint_hir_and_then};
use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths};
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
use rustc_hir::{
self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Safety, Impl, Item, ItemKind, UnsafeSource,
self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, Safety, UnsafeSource,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter;
use rustc_middle::traits::Reveal;
use rustc_middle::ty::{
self, ClauseKind, GenericArgKind, GenericParamDefKind, ParamEnv, Upcast, TraitPredicate, Ty, TyCtxt,
self, ClauseKind, GenericArgKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast,
};
use rustc_session::declare_lint_pass;
use rustc_span::def_id::LocalDefId;
@ -390,13 +390,17 @@ fn check_unsafe_derive_deserialize<'tcx>(
.map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local()))
.any(|imp| has_unsafe(cx, imp))
{
span_lint_and_help(
span_lint_hir_and_then(
cx,
UNSAFE_DERIVE_DESERIALIZE,
adt_hir_id,
item.span,
"you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
None,
"consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html",
|diag| {
diag.help(
"consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html",
);
},
);
}
}
@ -452,20 +456,27 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r
&& !has_non_exhaustive_attr(cx.tcx, *adt)
&& !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id)
&& let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id)
&& let Some(local_def_id) = adt.did().as_local()
// If all of our fields implement `Eq`, we can implement `Eq` too
&& adt
.all_fields()
.map(|f| f.ty(cx.tcx, args))
.all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, None, &[]))
{
span_lint_and_sugg(
span_lint_hir_and_then(
cx,
DERIVE_PARTIAL_EQ_WITHOUT_EQ,
cx.tcx.local_def_id_to_hir_id(local_def_id),
span.ctxt().outer_expn_data().call_site,
"you are deriving `PartialEq` and can implement `Eq`",
"consider deriving `Eq` as well",
"PartialEq, Eq".to_string(),
Applicability::MachineApplicable,
|diag| {
diag.span_suggestion(
span.ctxt().outer_expn_data().call_site,
"consider deriving `Eq` as well",
"PartialEq, Eq",
Applicability::MachineApplicable,
);
},
);
}
}

View File

@ -20,11 +20,11 @@ declare_clippy_lint! {
/// [aliases]: http://www.unicode.org/reports/tr24/tr24-31.html#Script_Value_Aliases
/// [supported_scripts]: https://www.unicode.org/iso15924/iso15924-codes.html
///
/// ### Why is this bad?
/// ### Why restrict this?
/// It may be not desired to have many different scripts for
/// identifiers in the codebase.
///
/// Note that if you only want to allow plain English, you might want to use
/// Note that if you only want to allow typical English, you might want to use
/// built-in [`non_ascii_idents`] lint instead.
///
/// [`non_ascii_idents`]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#non-ascii-idents

View File

@ -1,4 +1,4 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_with_applicability;
use rustc_data_structures::fx::FxHashSet;
use rustc_errors::{Applicability, SuggestionStyle};
@ -30,6 +30,7 @@ pub fn check(
word = tmp_word;
}
let original_len = word.len();
word = word.trim_start_matches(trim_pattern);
// Remove leading or trailing single `:` which may be part of a sentence.
@ -44,6 +45,25 @@ pub fn check(
continue;
}
// Ensure that all reachable matching closing parens are included as well.
let size_diff = original_len - word.len();
let mut open_parens = 0;
let mut close_parens = 0;
for c in word.chars() {
if c == '(' {
open_parens += 1;
} else if c == ')' {
close_parens += 1;
}
}
while close_parens < open_parens
&& let Some(tmp_word) = orig_word.get(size_diff..=(word.len() + size_diff))
&& tmp_word.ends_with(')')
{
word = tmp_word;
close_parens += 1;
}
// Adjust for the current word
let offset = word.as_ptr() as usize - text.as_ptr() as usize;
let span = Span::new(
@ -92,13 +112,15 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b
if let Ok(url) = Url::parse(word) {
// try to get around the fact that `foo::bar` parses as a valid URL
if !url.cannot_be_a_base() {
span_lint(
span_lint_and_sugg(
cx,
DOC_MARKDOWN,
span,
"you should put bare URLs between `<`/`>` or make a proper Markdown link",
"try",
format!("<{word}>"),
Applicability::MachineApplicable,
);
return;
}
}

View File

@ -261,7 +261,7 @@ declare_clippy_lint! {
/// Checks for the doc comments of publicly visible
/// safe functions and traits and warns if there is a `# Safety` section.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Safe functions and traits are safe to implement and therefore do not
/// need to describe safety preconditions that users are required to uphold.
///

View File

@ -52,9 +52,10 @@ declare_clippy_lint! {
/// Checks for usage of `std::mem::forget(t)` where `t` is
/// `Drop` or has a field that implements `Drop`.
///
/// ### Why is this bad?
/// `std::mem::forget(t)` prevents `t` from running its
/// destructor, possibly causing leaks.
/// ### Why restrict this?
/// `std::mem::forget(t)` prevents `t` from running its destructor, possibly causing leaks.
/// It is not possible to detect all means of creating leaks, but it may be desirable to
/// prohibit the simple ones.
///
/// ### Example
/// ```no_run

View File

@ -11,7 +11,7 @@ declare_clippy_lint! {
/// Checks for usage of if expressions with an `else if` branch,
/// but without a final `else` branch.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10).
///
/// ### Example

View File

@ -9,7 +9,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for empty `Drop` implementations.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Empty `Drop` implementations have no effect when dropping an instance of the type. They are
/// most likely useless. However, an empty `Drop` implementation prevents a type from being
/// destructured, which might be the intention behind adding the implementation as a marker.

View File

@ -7,32 +7,53 @@ use rustc_session::declare_lint_pass;
declare_clippy_lint! {
/// ### What it does
/// Checks for `enum`s with no variants.
/// Checks for `enum`s with no variants, which therefore are uninhabited types
/// (cannot be instantiated).
///
/// As of this writing, the `never_type` is still a
/// nightly-only experimental API. Therefore, this lint is only triggered
/// if the `never_type` is enabled.
/// As of this writing, the `never_type` is still a nightly-only experimental API.
/// Therefore, this lint is only triggered if `#![feature(never_type)]` is enabled.
///
/// ### Why is this bad?
/// If you want to introduce a type which
/// can't be instantiated, you should use `!` (the primitive type "never"),
/// or a wrapper around it, because `!` has more extensive
/// compiler support (type inference, etc...) and wrappers
/// around it are the conventional way to define an uninhabited type.
/// For further information visit [never type documentation](https://doc.rust-lang.org/std/primitive.never.html)
/// * If you only want a type which cant be instantiated, you should use [`!`]
/// (the primitive type "never"), because [`!`] has more extensive compiler support
/// (type inference, etc.) and implementations of common traits.
///
/// * If you need to introduce a distinct type, consider using a [newtype] `struct`
/// containing [`!`] instead (`struct MyType(pub !)`), because it is more idiomatic
/// to use a `struct` rather than an `enum` when an `enum` is unnecessary.
///
/// If you do this, note that the [visibility] of the [`!`] field determines whether
/// the uninhabitedness is visible in documentation, and whether it can be pattern
/// matched to mark code unreachable. If the field is not visible, then the struct
/// acts like any other struct with private fields.
///
/// * If the enum has no variants only because all variants happen to be
/// [disabled by conditional compilation][cfg], then it would be appropriate
/// to allow the lint, with `#[allow(empty_enum)]`.
///
/// For further information, visit
/// [the never types documentation][`!`].
///
/// ### Example
/// ```no_run
/// enum Test {}
/// enum CannotExist {}
/// ```
///
/// Use instead:
/// ```no_run
/// #![feature(never_type)]
///
/// struct Test(!);
/// /// Use the `!` type directly...
/// type CannotExist = !;
///
/// /// ...or define a newtype which is distinct.
/// struct CannotExist2(pub !);
/// ```
///
/// [`!`]: https://doc.rust-lang.org/std/primitive.never.html
/// [cfg]: https://doc.rust-lang.org/reference/conditional-compilation.html
/// [newtype]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction
/// [visibility]: https://doc.rust-lang.org/reference/visibility-and-privacy.html
#[clippy::version = "pre 1.29.0"]
pub EMPTY_ENUM,
pedantic,

View File

@ -11,16 +11,23 @@ declare_clippy_lint! {
/// ### What it does
/// Finds structs without fields (a so-called "empty struct") that are declared with brackets.
///
/// ### Why is this bad?
/// Empty brackets after a struct declaration can be omitted.
/// ### Why restrict this?
/// Empty brackets after a struct declaration can be omitted,
/// and it may be desirable to do so consistently for style.
///
/// However, removing the brackets also introduces a public constant named after the struct,
/// so this is not just a syntactic simplification but an an API change, and adding them back
/// is a *breaking* API change.
///
/// ### Example
/// ```no_run
/// struct Cookie {}
/// struct Biscuit();
/// ```
/// Use instead:
/// ```no_run
/// struct Cookie;
/// struct Biscuit;
/// ```
#[clippy::version = "1.62.0"]
pub EMPTY_STRUCTS_WITH_BRACKETS,
@ -32,14 +39,20 @@ declare_clippy_lint! {
/// ### What it does
/// Finds enum variants without fields that are declared with empty brackets.
///
/// ### Why is this bad?
/// Empty brackets while defining enum variants are redundant and can be omitted.
/// ### Why restrict this?
/// Empty brackets after a enum variant declaration are redundant and can be omitted,
/// and it may be desirable to do so consistently for style.
///
/// However, removing the brackets also introduces a public constant named after the variant,
/// so this is not just a syntactic simplification but an an API change, and adding them back
/// is a *breaking* API change.
///
/// ### Example
/// ```no_run
/// enum MyEnum {
/// HasData(u8),
/// HasNoData(), // redundant parentheses
/// HasNoData(), // redundant parentheses
/// NoneHereEither {}, // redundant braces
/// }
/// ```
///
@ -48,6 +61,7 @@ declare_clippy_lint! {
/// enum MyEnum {
/// HasData(u8),
/// HasNoData,
/// NoneHereEither,
/// }
/// ```
#[clippy::version = "1.77.0"]

View File

@ -13,8 +13,9 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for the usage of the `to_ne_bytes` method and/or the function `from_ne_bytes`.
///
/// ### Why is this bad?
/// It's not, but some may prefer to specify the target endianness explicitly.
/// ### Why restrict this?
/// To ensure use of explicitly chosen endianness rather than the targets endianness,
/// such as when implementing network protocols or file formats rather than FFI.
///
/// ### Example
/// ```rust,ignore
@ -31,9 +32,8 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for the usage of the `to_le_bytes` method and/or the function `from_le_bytes`.
///
/// ### Why is this bad?
/// It's not, but some may wish to lint usage of this method, either to suggest using the host
/// endianness or big endian.
/// ### Why restrict this?
/// To ensure use of big endian or the targets endianness rather than little endian.
///
/// ### Example
/// ```rust,ignore
@ -50,9 +50,8 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for the usage of the `to_be_bytes` method and/or the function `from_be_bytes`.
///
/// ### Why is this bad?
/// It's not, but some may wish to lint usage of this method, either to suggest using the host
/// endianness or little endian.
/// ### Why restrict this?
/// To ensure use of little endian or the targets endianness rather than big endian.
///
/// ### Example
/// ```rust,ignore

View File

@ -12,7 +12,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for types named `Error` that implement `Error`.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// It can become confusing when a codebase has 20 types all named `Error`, requiring either
/// aliasing them in the `use` statement or qualifying them like `my_module::Error`. This
/// hinders comprehension, as it requires you to memorize every variation of importing `Error`

View File

@ -10,10 +10,10 @@ declare_clippy_lint! {
/// ### What it does
/// Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`
///
/// ### Why is this bad?
/// Exhaustive enums are typically fine, but a project which does
/// not wish to make a stability commitment around exported enums may wish to
/// disable them by default.
/// ### Why restrict this?
/// Making an `enum` exhaustive is a stability commitment: adding a variant is a breaking change.
/// A project may wish to ensure that there are no exhaustive enums or that every exhaustive
/// `enum` is explicitly `#[allow]`ed.
///
/// ### Example
/// ```no_run
@ -40,10 +40,10 @@ declare_clippy_lint! {
/// ### What it does
/// Warns on any exported `struct`s that are not tagged `#[non_exhaustive]`
///
/// ### Why is this bad?
/// Exhaustive structs are typically fine, but a project which does
/// not wish to make a stability commitment around exported structs may wish to
/// disable them by default.
/// ### Why restrict this?
/// Making a `struct` exhaustive is a stability commitment: adding a field is a breaking change.
/// A project may wish to ensure that there are no exhaustive structs or that every exhaustive
/// `struct` is explicitly `#[allow]`ed.
///
/// ### Example
/// ```no_run

View File

@ -9,11 +9,13 @@ declare_clippy_lint! {
/// ### What it does
/// Detects calls to the `exit()` function which terminates the program.
///
/// ### Why is this bad?
/// Exit terminates the program at the location it is called. For unrecoverable
/// errors `panics` should be used to provide a stacktrace and potentially other
/// information. A normal termination or one with an error code should happen in
/// the main function.
/// ### Why restrict this?
/// `exit()` immediately terminates the program with no information other than an exit code.
/// This provides no means to troubleshoot a problem, and may be an unexpected side effect.
///
/// Codebases may use this lint to require that all exits are performed either by panicking
/// (which produces a message, a code location, and optionally a backtrace)
/// or by returning from `main()` (which is a single place to look).
///
/// ### Example
/// ```no_run

View File

@ -38,9 +38,9 @@ declare_clippy_lint! {
/// Checks for whole number float literals that
/// cannot be represented as the underlying type without loss.
///
/// ### Why is this bad?
/// Rust will silently lose precision during
/// conversion to a float.
/// ### Why restrict this?
/// If the value was intended to be exact, it will not be.
/// This may be especially surprising when the lost precision is to the left of the decimal point.
///
/// ### Example
/// ```no_run

View File

@ -11,7 +11,7 @@ declare_clippy_lint! {
/// Detects cases where the result of a `format!` call is
/// appended to an existing `String`.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Introduces an extra, avoidable heap allocation.
///
/// ### Known problems

View File

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet;
use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, ExprKind, FnDecl, Safety, ImplicitSelfKind};
use rustc_hir::{Body, ExprKind, FnDecl, ImplicitSelfKind, Safety};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::Span;

View File

@ -338,8 +338,10 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
/// Lints when `impl Trait` is being used in a function's parameters.
/// ### Why is this bad?
/// Turbofish syntax (`::<>`) cannot be used when `impl Trait` is being used, making `impl Trait` less powerful. Readability may also be a factor.
///
/// ### Why restrict this?
/// Turbofish syntax (`::<>`) cannot be used to specify the type of an `impl Trait` parameter,
/// making `impl Trait` less powerful. Readability may also be a factor.
///
/// ### Example
/// ```no_run
@ -366,9 +368,8 @@ declare_clippy_lint! {
/// Lints when the name of function parameters from trait impl is
/// different than its default implementation.
///
/// ### Why is this bad?
/// Using the default name for parameters of a trait method is often
/// more desirable for consistency's sake.
/// ### Why restrict this?
/// Using the default name for parameters of a trait method is more consistent.
///
/// ### Example
/// ```rust

View File

@ -15,7 +15,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for if-else that could be written using either `bool::then` or `bool::then_some`.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Looks a little redundant. Using `bool::then` is more concise and incurs no loss of clarity.
/// For simple calculations and known values, use `bool::then_some`, which is eagerly evaluated
/// in comparison to `bool::then`.

View File

@ -16,12 +16,13 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for missing return statements at the end of a block.
///
/// ### Why is this bad?
/// Actually omitting the return keyword is idiomatic Rust code. Programmers
/// coming from other languages might prefer the expressiveness of `return`. It's possible to miss
/// the last returning statement because the only difference is a missing `;`. Especially in bigger
/// code with multiple return paths having a `return` keyword makes it easier to find the
/// corresponding statements.
/// ### Why restrict this?
/// Omitting the return keyword whenever possible is idiomatic Rust code, but:
///
/// * Programmers coming from other languages might prefer the expressiveness of `return`.
/// * It's possible to miss the last returning statement because the only difference is a missing `;`.
/// * Especially in bigger code with multiple return paths, having a `return` keyword makes it easier to find the
/// corresponding statements.
///
/// ### Example
/// ```no_run

View File

@ -45,9 +45,10 @@ declare_clippy_lint! {
/// does report on arrays if we can tell that slicing operations are in bounds and does not
/// lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint.
///
/// ### Why is this bad?
/// Indexing and slicing can panic at runtime and there are
/// safe alternatives.
/// ### Why restrict this?
/// To avoid implicit panics from indexing and slicing.
/// There are “checked” alternatives which do not panic, and can be used with `unwrap()` to make
/// an explicit panic when it is desired.
///
/// ### Example
/// ```rust,no_run

View File

@ -14,7 +14,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for multiple inherent implementations of a struct
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Splitting the implementation of a type makes the code harder to navigate.
///
/// ### Example

View File

@ -1,5 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Expr, ExprKind};
@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields {
snippet_with_applicability(cx, path.span(), "..", &mut appl),
expr_spans
.into_iter_sorted()
.map(|(_, span)| snippet_with_applicability(cx, span, "..", &mut appl))
.map(|(_, span)| snippet_with_context(cx, span, path.span().ctxt(), "..", &mut appl).0)
.intersperse(Cow::Borrowed(", "))
.collect::<String>()
);

View File

@ -7,10 +7,10 @@ use rustc_session::declare_lint_pass;
declare_clippy_lint! {
/// ### What it does
/// Checks for the usage of division (/) and remainder (%) operations
/// when performed on any integer types using the default Div and Rem trait implementations.
/// Checks for the usage of division (`/`) and remainder (`%`) operations
/// when performed on any integer types using the default `Div` and `Rem` trait implementations.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// In cryptographic contexts, division can result in timing sidechannel vulnerabilities,
/// and needs to be replaced with constant-time code instead (e.g. Barrett reduction).
///

View File

@ -14,8 +14,8 @@ declare_clippy_lint! {
/// ### What it does
/// This is a restriction lint which prevents the use of hash types (i.e., `HashSet` and `HashMap`) in for loops.
///
/// ### Why is this bad?
/// Because hash types are unordered, when iterated through such as in a for loop, the values are returned in
/// ### Why restrict this?
/// Because hash types are unordered, when iterated through such as in a `for` loop, the values are returned in
/// an undefined order. As a result, on redundant systems this may cause inconsistencies and anomalies.
/// In addition, the unknown order of the elements may reduce readability or introduce other undesired
/// side effects.

View File

@ -10,10 +10,12 @@ use rustc_span::sym;
declare_clippy_lint! {
/// ### What it does
/// Checks for the inclusion of large files via `include_bytes!()`
/// and `include_str!()`
/// or `include_str!()`.
///
/// ### Why is this bad?
/// Including large files can increase the size of the binary
/// ### Why restrict this?
/// Including large files can undesirably increase the size of the binary produced by the compiler.
/// This lint may be used to catch mistakes where an unexpectedly large file is included, or
/// temporarily to obtain a list of all large files.
///
/// ### Example
/// ```rust,ignore

View File

@ -12,9 +12,8 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for `let _ = <expr>` where expr is `#[must_use]`
///
/// ### Why is this bad?
/// It's better to explicitly handle the value of a `#[must_use]`
/// expr
/// ### Why restrict this?
/// To ensure that all `#[must_use]` types are used rather than ignored.
///
/// ### Example
/// ```no_run
@ -96,8 +95,8 @@ declare_clippy_lint! {
/// Checks for `let _ = <expr>` without a type annotation, and suggests to either provide one,
/// or remove the `let` keyword altogether.
///
/// ### Why is this bad?
/// The `let _ = <expr>` expression ignores the value of `<expr>` but will remain doing so even
/// ### Why restrict this?
/// The `let _ = <expr>` expression ignores the value of `<expr>`, but will continue to do so even
/// if the type were to change, thus potentially introducing subtle bugs. By supplying a type
/// annotation, one will be forced to re-visit the decision to ignore the value in such cases.
///

View File

@ -132,8 +132,8 @@ declare_clippy_lint! {
/// ### What it does
/// Warns if there is a better representation for a numeric literal.
///
/// ### Why is this bad?
/// Especially for big powers of 2 a hexadecimal representation is more
/// ### Why restrict this?
/// Especially for big powers of 2, a hexadecimal representation is usually more
/// readable than a decimal representation.
///
/// ### Example

View File

@ -675,9 +675,9 @@ declare_clippy_lint! {
/// Checks for infinite loops in a function where the return type is not `!`
/// and lint accordingly.
///
/// ### Why is this bad?
/// A loop should be gently exited somewhere, or at least mark its parent function as
/// never return (`!`).
/// ### Why restrict this?
/// Making the return type `!` serves as documentation that the function does not return.
/// If the function is not intended to loop infinitely, then this lint may detect a bug.
///
/// ### Example
/// ```no_run,ignore

View File

@ -260,7 +260,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for wildcard enum matches using `_`.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// New enum variants added by library updates can be missed.
///
/// ### Known problems
@ -435,7 +435,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Correctness and readability. It's like having a wildcard pattern after
/// matching all enum variants explicitly.
///
@ -861,7 +861,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `Err(x)?`.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// The `?` operator is designed to allow calls that
/// can fail to be easily chained. For example, `foo()?.bar()` or
/// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will

View File

@ -1,12 +1,17 @@
use std::ops::ControlFlow;
use crate::FxHashSet;
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{indent_of, snippet};
use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy};
use clippy_utils::{get_attr, is_lint_allowed};
use itertools::Itertools;
use rustc_ast::Mutability;
use rustc_errors::{Applicability, Diag};
use rustc_hir::intravisit::{walk_expr, Visitor};
use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty::{GenericArgKind, Ty};
use rustc_middle::ty::{GenericArgKind, Region, RegionKind, Ty, TyCtxt, TypeVisitable, TypeVisitor};
use rustc_span::Span;
use super::SIGNIFICANT_DROP_IN_SCRUTINEE;
@ -22,43 +27,34 @@ pub(super) fn check<'tcx>(
return;
}
if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, scrutinee, source) {
for found in suggestions {
span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| {
set_diagnostic(diag, cx, expr, found);
let s = Span::new(expr.span.hi(), expr.span.hi(), expr.span.ctxt(), None);
diag.span_label(s, "temporary lives until here");
for span in has_significant_drop_in_arms(cx, arms) {
diag.span_label(span, "another value with significant `Drop` created here");
}
diag.note("this might lead to deadlocks or other unexpected behavior");
});
}
let (suggestions, message) = has_significant_drop_in_scrutinee(cx, scrutinee, source);
for found in suggestions {
span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| {
set_diagnostic(diag, cx, expr, found);
let s = Span::new(expr.span.hi(), expr.span.hi(), expr.span.ctxt(), None);
diag.span_label(s, "temporary lives until here");
for span in has_significant_drop_in_arms(cx, arms) {
diag.span_label(span, "another value with significant `Drop` created here");
}
diag.note("this might lead to deadlocks or other unexpected behavior");
});
}
}
fn set_diagnostic<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) {
if found.lint_suggestion == LintSuggestion::MoveAndClone {
// If our suggestion is to move and clone, then we want to leave it to the user to
// decide how to address this lint, since it may be that cloning is inappropriate.
// Therefore, we won't to emit a suggestion.
return;
}
let original = snippet(cx, found.found_span, "..");
let trailing_indent = " ".repeat(indent_of(cx, found.found_span).unwrap_or(0));
let replacement = if found.lint_suggestion == LintSuggestion::MoveAndDerefToCopy {
format!("let value = *{original};\n{trailing_indent}")
} else if found.is_unit_return_val {
// If the return value of the expression to be moved is unit, then we don't need to
// capture the result in a temporary -- we can just replace it completely with `()`.
format!("{original};\n{trailing_indent}")
} else {
format!("let value = {original};\n{trailing_indent}")
let replacement = {
let (def_part, deref_part) = if found.is_unit_return_val {
("", String::new())
} else {
("let value = ", "*".repeat(found.peel_ref_times))
};
format!("{def_part}{deref_part}{original};\n{trailing_indent}")
};
let suggestion_message = if found.lint_suggestion == LintSuggestion::MoveOnly {
let suggestion_message = if found.peel_ref_times == 0 {
"try moving the temporary above the match"
} else {
"try moving the temporary above the match and create a copy"
@ -66,8 +62,11 @@ fn set_diagnostic<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &
let scrutinee_replacement = if found.is_unit_return_val {
"()".to_owned()
} else {
} else if found.peel_ref_times == 0 {
"value".to_owned()
} else {
let ref_part = "&".repeat(found.peel_ref_times);
format!("({ref_part}value)")
};
diag.multipart_suggestion(
@ -86,20 +85,18 @@ fn has_significant_drop_in_scrutinee<'tcx>(
cx: &LateContext<'tcx>,
scrutinee: &'tcx Expr<'tcx>,
source: MatchSource,
) -> Option<(Vec<FoundSigDrop>, &'static str)> {
) -> (Vec<FoundSigDrop>, &'static str) {
let mut helper = SigDropHelper::new(cx);
let scrutinee = match (source, &scrutinee.kind) {
(MatchSource::ForLoopDesugar, ExprKind::Call(_, [e])) => e,
_ => scrutinee,
};
helper.find_sig_drop(scrutinee).map(|drops| {
let message = if source == MatchSource::Normal {
"temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression"
} else {
"temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression"
};
(drops, message)
})
let message = if source == MatchSource::Normal {
"temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression"
} else {
"temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression"
};
(helper.find_sig_drop(scrutinee), message)
}
struct SigDropChecker<'a, 'tcx> {
@ -172,205 +169,248 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
enum SigDropHolder {
/// No values with significant drop present in this expression.
///
/// Expressions that we've emited lints do not count.
None,
/// Some field in this expression references to values with significant drop.
///
/// Example: `(1, &data.lock().field)`.
PackedRef,
/// The value of this expression references to values with significant drop.
///
/// Example: `data.lock().field`.
DirectRef,
/// This expression should be moved out to avoid significant drop in scrutinee.
Moved,
}
impl Default for SigDropHolder {
fn default() -> Self {
Self::None
}
}
struct SigDropHelper<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
is_chain_end: bool,
has_significant_drop: bool,
current_sig_drop: Option<FoundSigDrop>,
sig_drop_spans: Option<Vec<FoundSigDrop>>,
special_handling_for_binary_op: bool,
parent_expr: Option<&'tcx Expr<'tcx>>,
sig_drop_holder: SigDropHolder,
sig_drop_spans: Vec<FoundSigDrop>,
sig_drop_checker: SigDropChecker<'a, 'tcx>,
}
#[expect(clippy::enum_variant_names)]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
enum LintSuggestion {
MoveOnly,
MoveAndDerefToCopy,
MoveAndClone,
}
#[derive(Clone, Copy)]
#[derive(Clone, Copy, Debug)]
struct FoundSigDrop {
found_span: Span,
is_unit_return_val: bool,
lint_suggestion: LintSuggestion,
peel_ref_times: usize,
}
impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
fn new(cx: &'a LateContext<'tcx>) -> SigDropHelper<'a, 'tcx> {
SigDropHelper {
cx,
is_chain_end: true,
has_significant_drop: false,
current_sig_drop: None,
sig_drop_spans: None,
special_handling_for_binary_op: false,
parent_expr: None,
sig_drop_holder: SigDropHolder::None,
sig_drop_spans: Vec::new(),
sig_drop_checker: SigDropChecker::new(cx),
}
}
fn find_sig_drop(&mut self, match_expr: &'tcx Expr<'_>) -> Option<Vec<FoundSigDrop>> {
fn find_sig_drop(&mut self, match_expr: &'tcx Expr<'_>) -> Vec<FoundSigDrop> {
self.visit_expr(match_expr);
// If sig drop spans is empty but we found a significant drop, it means that we didn't find
// a type that was trivially copyable as we moved up the chain after finding a significant
// drop, so move the entire scrutinee.
if self.has_significant_drop && self.sig_drop_spans.is_none() {
self.try_setting_current_suggestion(match_expr, true);
self.move_current_suggestion();
}
self.sig_drop_spans.take()
core::mem::take(&mut self.sig_drop_spans)
}
fn replace_current_sig_drop(
&mut self,
found_span: Span,
is_unit_return_val: bool,
lint_suggestion: LintSuggestion,
) {
self.current_sig_drop.replace(FoundSigDrop {
fn replace_current_sig_drop(&mut self, found_span: Span, is_unit_return_val: bool, peel_ref_times: usize) {
self.sig_drop_spans.clear();
self.sig_drop_spans.push(FoundSigDrop {
found_span,
is_unit_return_val,
lint_suggestion,
peel_ref_times,
});
}
/// This will try to set the current suggestion (so it can be moved into the suggestions vec
/// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us
/// an opportunity to look for another type in the chain that will be trivially copyable.
/// However, if we are at the end of the chain, we want to accept whatever is there. (The
/// suggestion won't actually be output, but the diagnostic message will be output, so the user
/// can determine the best way to handle the lint.)
fn try_setting_current_suggestion(&mut self, expr: &'tcx Expr<'_>, allow_move_and_clone: bool) {
if self.current_sig_drop.is_some() {
return;
fn try_move_sig_drop(&mut self, expr: &'tcx Expr<'_>, parent_expr: &'tcx Expr<'_>) {
if self.sig_drop_holder == SigDropHolder::Moved {
self.sig_drop_holder = SigDropHolder::None;
}
let ty = self.cx.typeck_results().expr_ty(expr);
if ty.is_ref() {
// We checked that the type was ref, so builtin_deref will return Some,
// but let's avoid any chance of an ICE.
if let Some(ty) = ty.builtin_deref(true) {
if ty.is_trivially_pure_clone_copy() {
self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndDerefToCopy);
} else if allow_move_and_clone {
self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone);
}
if self.sig_drop_holder == SigDropHolder::DirectRef {
self.sig_drop_holder = SigDropHolder::PackedRef;
self.try_move_sig_drop_direct_ref(expr, parent_expr);
} else if self.sig_drop_checker.is_sig_drop_expr(expr) {
// The values with significant drop can be moved to some other functions. For example, consider
// `drop(data.lock())`. We use `SigDropHolder::None` here to avoid emitting lints in such scenarios.
self.sig_drop_holder = SigDropHolder::None;
self.try_move_sig_drop_direct_ref(expr, parent_expr);
}
if self.sig_drop_holder != SigDropHolder::None {
let parent_ty = self.cx.typeck_results().expr_ty(parent_expr);
if !ty_has_erased_regions(parent_ty) && !parent_expr.is_syntactic_place_expr() {
self.replace_current_sig_drop(parent_expr.span, parent_ty.is_unit(), 0);
self.sig_drop_holder = SigDropHolder::Moved;
}
let (peel_ref_ty, peel_ref_times) = ty_peel_refs(parent_ty);
if !ty_has_erased_regions(peel_ref_ty) && is_copy(self.cx, peel_ref_ty) {
self.replace_current_sig_drop(parent_expr.span, peel_ref_ty.is_unit(), peel_ref_times);
self.sig_drop_holder = SigDropHolder::Moved;
}
} else if ty.is_trivially_pure_clone_copy() {
self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveOnly);
} else if allow_move_and_clone {
self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone);
}
}
fn move_current_suggestion(&mut self) {
if let Some(current) = self.current_sig_drop.take() {
self.sig_drop_spans.get_or_insert_with(Vec::new).push(current);
fn try_move_sig_drop_direct_ref(&mut self, expr: &'tcx Expr<'_>, parent_expr: &'tcx Expr<'_>) {
let arg_idx = match parent_expr.kind {
ExprKind::MethodCall(_, receiver, exprs, _) => std::iter::once(receiver)
.chain(exprs.iter())
.find_position(|ex| ex.hir_id == expr.hir_id)
.map(|(idx, _)| idx),
ExprKind::Call(_, exprs) => exprs
.iter()
.find_position(|ex| ex.hir_id == expr.hir_id)
.map(|(idx, _)| idx),
ExprKind::Binary(_, lhs, rhs) | ExprKind::AssignOp(_, lhs, rhs) => [lhs, rhs]
.iter()
.find_position(|ex| ex.hir_id == expr.hir_id)
.map(|(idx, _)| idx),
ExprKind::Unary(_, ex) => (ex.hir_id == expr.hir_id).then_some(0),
_ => {
// Here we assume that all other expressions create or propagate the reference to the value with
// significant drop.
self.sig_drop_holder = SigDropHolder::DirectRef;
return;
},
};
let Some(arg_idx) = arg_idx else {
return;
};
let fn_sig = if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) {
self.cx.tcx.fn_sig(def_id).instantiate_identity()
} else {
return;
};
let input_re = if let Some(input_ty) = fn_sig.skip_binder().inputs().get(arg_idx)
&& let rustc_middle::ty::Ref(input_re, _, _) = input_ty.kind()
{
input_re
} else {
return;
};
// Late bound lifetime parameters are not related to any constraints, so we can track them in a very
// simple manner. For other lifetime parameters, we give up and update the state to `PackedRef`.
let RegionKind::ReBound(_, input_re_bound) = input_re.kind() else {
self.sig_drop_holder = SigDropHolder::PackedRef;
return;
};
let contains_input_re = |re_bound| {
if re_bound == input_re_bound {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
};
let output_ty = fn_sig.skip_binder().output();
if let rustc_middle::ty::Ref(output_re, peel_ref_ty, _) = output_ty.kind()
&& input_re == output_re
&& for_each_top_level_late_bound_region(*peel_ref_ty, contains_input_re).is_continue()
{
// We're lucky! The output type is still a direct reference to the value with significant drop.
self.sig_drop_holder = SigDropHolder::DirectRef;
} else if for_each_top_level_late_bound_region(output_ty, contains_input_re).is_continue() {
// The lifetime to the value with significant drop goes away. So we can emit a lint that suggests to
// move the expression out.
self.replace_current_sig_drop(parent_expr.span, output_ty.is_unit(), 0);
self.sig_drop_holder = SigDropHolder::Moved;
} else {
// TODO: The lifetime is still there but it's for a inner type. For instance, consider
// `Some(&mutex.lock().field)`, which has a type of `Option<&u32>`. How to address this scenario?
self.sig_drop_holder = SigDropHolder::PackedRef;
}
}
}
fn ty_peel_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
let mut n = 0;
while let rustc_middle::ty::Ref(_, new_ty, Mutability::Not) = ty.kind() {
ty = *new_ty;
n += 1;
}
(ty, n)
}
fn ty_has_erased_regions(ty: Ty<'_>) -> bool {
struct V;
impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for V {
type Result = ControlFlow<()>;
fn visit_region(&mut self, region: Region<'tcx>) -> Self::Result {
if region.is_erased() {
ControlFlow::Break(())
} else {
ControlFlow::Continue(())
}
}
}
fn visit_exprs_for_binary_ops(
&mut self,
left: &'tcx Expr<'_>,
right: &'tcx Expr<'_>,
is_unit_return_val: bool,
span: Span,
) {
self.special_handling_for_binary_op = true;
self.visit_expr(left);
self.visit_expr(right);
// If either side had a significant drop, suggest moving the entire scrutinee to avoid
// unnecessary copies and to simplify cases where both sides have significant drops.
if self.has_significant_drop {
self.replace_current_sig_drop(span, is_unit_return_val, LintSuggestion::MoveOnly);
}
self.special_handling_for_binary_op = false;
}
ty.visit_with(&mut V).is_break()
}
impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
if !self.is_chain_end && self.sig_drop_checker.is_sig_drop_expr(ex) {
self.has_significant_drop = true;
// We've emited a lint on some neighborhood expression. That lint will suggest to move out the
// _parent_ expression (not the expression itself). Since we decide to move out the parent
// expression, it is pointless to continue to process the current expression.
if self.sig_drop_holder == SigDropHolder::Moved {
return;
}
self.is_chain_end = false;
// These states are of neighborhood expressions. We save and clear them here, and we'll later merge
// the states of the current expression with them at the end of the method.
let sig_drop_holder_before = core::mem::take(&mut self.sig_drop_holder);
let sig_drop_spans_before = core::mem::take(&mut self.sig_drop_spans);
let parent_expr_before = self.parent_expr.replace(ex);
match ex.kind {
ExprKind::MethodCall(_, expr, ..) => {
self.visit_expr(expr);
}
ExprKind::Binary(_, left, right) => {
self.visit_exprs_for_binary_ops(left, right, false, ex.span);
}
ExprKind::Assign(left, right, _) | ExprKind::AssignOp(_, left, right) => {
self.visit_exprs_for_binary_ops(left, right, true, ex.span);
}
ExprKind::Tup(exprs) => {
for expr in exprs {
self.visit_expr(expr);
if self.has_significant_drop {
// We may have not have set current_sig_drop if all the suggestions were
// MoveAndClone, so add this tuple item's full expression in that case.
if self.current_sig_drop.is_none() {
self.try_setting_current_suggestion(expr, true);
}
// Skip blocks because values in blocks will be dropped as usual.
ExprKind::Block(..) => (),
_ => walk_expr(self, ex),
}
// Now we are guaranteed to have something, so add it to the final vec.
self.move_current_suggestion();
}
// Reset `has_significant_drop` after each tuple expression so we can look for
// additional cases.
self.has_significant_drop = false;
}
if self.sig_drop_spans.is_some() {
self.has_significant_drop = true;
}
}
ExprKind::Array(..) |
ExprKind::Call(..) |
ExprKind::Unary(..) |
ExprKind::If(..) |
ExprKind::Match(..) |
ExprKind::Field(..) |
ExprKind::Index(..) |
ExprKind::Ret(..) |
ExprKind::Become(..) |
ExprKind::Repeat(..) |
ExprKind::Yield(..) => walk_expr(self, ex),
ExprKind::AddrOf(_, _, _) |
ExprKind::Block(_, _) |
ExprKind::Break(_, _) |
ExprKind::Cast(_, _) |
// Don't want to check the closure itself, only invocation, which is covered by MethodCall
ExprKind::Closure { .. } |
ExprKind::ConstBlock(_) |
ExprKind::Continue(_) |
ExprKind::DropTemps(_) |
ExprKind::Err(_) |
ExprKind::InlineAsm(_) |
ExprKind::OffsetOf(_, _) |
ExprKind::Let(_) |
ExprKind::Lit(_) |
ExprKind::Loop(_, _, _, _) |
ExprKind::Path(_) |
ExprKind::Struct(_, _, _) |
ExprKind::Type(_, _) => {
return;
if let Some(parent_ex) = parent_expr_before {
match parent_ex.kind {
ExprKind::Assign(lhs, _, _) | ExprKind::AssignOp(_, lhs, _)
if lhs.hir_id == ex.hir_id && self.sig_drop_holder == SigDropHolder::Moved =>
{
// Never move out only the assignee. Instead, we should always move out the whole assigment.
self.replace_current_sig_drop(parent_ex.span, true, 0);
},
_ => {
self.try_move_sig_drop(ex, parent_ex);
},
}
}
// Once a significant temporary has been found, we need to go back up at least 1 level to
// find the span to extract for replacement, so the temporary gets dropped. However, for
// binary ops, we want to move the whole scrutinee so we avoid unnecessary copies and to
// simplify cases where both sides have significant drops.
if self.has_significant_drop && !self.special_handling_for_binary_op {
self.try_setting_current_suggestion(ex, false);
self.sig_drop_holder = std::cmp::max(self.sig_drop_holder, sig_drop_holder_before);
// We do not need those old spans in neighborhood expressions if we emit a lint that suggests to
// move out the _parent_ expression (i.e., `self.sig_drop_holder == SigDropHolder::Moved`).
if self.sig_drop_holder != SigDropHolder::Moved {
let mut sig_drop_spans = sig_drop_spans_before;
sig_drop_spans.append(&mut self.sig_drop_spans);
self.sig_drop_spans = sig_drop_spans;
}
self.parent_expr = parent_expr_before;
}
}

View File

@ -1,8 +1,12 @@
use std::iter::once;
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet;
use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::def_id::DefId;
use rustc_hir::hir_id::HirId;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind, Node};
use rustc_lint::LateContext;
@ -25,7 +29,29 @@ impl IterType {
}
}
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) {
fn is_arg_ty_unified_in_fn<'tcx>(
cx: &LateContext<'tcx>,
fn_id: DefId,
arg_id: HirId,
args: impl IntoIterator<Item = &'tcx Expr<'tcx>>,
) -> bool {
let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity();
let arg_id_in_args = args.into_iter().position(|e| e.hir_id == arg_id).unwrap();
let arg_ty_in_args = fn_sig.input(arg_id_in_args).skip_binder();
cx.tcx.predicates_of(fn_id).predicates.iter().any(|(clause, _)| {
clause
.as_projection_clause()
.and_then(|p| p.map_bound(|p| p.term.ty()).transpose())
.is_some_and(|ty| ty.skip_binder() == arg_ty_in_args)
}) || fn_sig
.inputs()
.iter()
.enumerate()
.any(|(i, ty)| i != arg_id_in_args && ty.skip_binder().walk().any(|arg| arg.as_type() == Some(arg_ty_in_args)))
}
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: &str, recv: &'tcx Expr<'tcx>) {
let item = match recv.kind {
ExprKind::Array([]) => None,
ExprKind::Array([e]) => Some(e),
@ -43,6 +69,25 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, re
let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) {
Some((Node::Expr(parent), child_id)) => match parent.kind {
ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false,
ExprKind::Call(
Expr {
kind: ExprKind::Path(path),
hir_id,
..
},
args,
) => cx
.typeck_results()
.qpath_res(path, *hir_id)
.opt_def_id()
.filter(|fn_id| cx.tcx.def_kind(fn_id).is_fn_like())
.is_some_and(|fn_id| is_arg_ty_unified_in_fn(cx, fn_id, child_id, args)),
ExprKind::MethodCall(_name, recv, args, _span) => is_arg_ty_unified_in_fn(
cx,
cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap(),
child_id,
once(recv).chain(args.iter()),
),
ExprKind::If(_, _, _)
| ExprKind::Match(_, _, _)
| ExprKind::Closure(_)

View File

@ -257,7 +257,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for `.unwrap()` or `.unwrap_err()` calls on `Result`s and `.unwrap()` call on `Option`s.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// It is better to handle the `None` or `Err` case,
/// or at least call `.expect(_)` with a more helpful message. Still, for a lot of
/// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is
@ -333,7 +333,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Usually it is better to handle the `None` or `Err` case.
/// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why
/// this lint is `Allow` by default.
@ -1029,8 +1029,8 @@ declare_clippy_lint! {
/// (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified
/// function syntax instead (e.g., `Rc::clone(foo)`).
///
/// ### Why is this bad?
/// Calling '.clone()' on an Rc, Arc, or Weak
/// ### Why restrict this?
/// Calling `.clone()` on an `Rc`, `Arc`, or `Weak`
/// can obscure the fact that only the pointer is being cloned, not the underlying
/// data.
///
@ -1051,7 +1051,7 @@ declare_clippy_lint! {
#[clippy::version = "pre 1.29.0"]
pub CLONE_ON_REF_PTR,
restriction,
"using 'clone' on a ref-counted pointer"
"using `clone` on a ref-counted pointer"
}
declare_clippy_lint! {
@ -1359,7 +1359,7 @@ declare_clippy_lint! {
/// Checks for usage of `.get().unwrap()` (or
/// `.get_mut().unwrap`) on a standard library type which implements `Index`
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Using the Index trait (`[]`) is more clear and more
/// concise.
///
@ -1743,7 +1743,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for `FileType::is_file()`.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// When people testing a file type with `FileType::is_file`
/// they are testing whether a path is something they can get bytes from. But
/// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
@ -2688,8 +2688,9 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for instances of `map_err(|_| Some::Enum)`
///
/// ### Why is this bad?
/// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
/// ### Why restrict this?
/// This `map_err` throws away the original error rather than allowing the enum to
/// contain and report the cause of the error.
///
/// ### Example
/// Before:
@ -3145,7 +3146,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for usage of File::read_to_end and File::read_to_string.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
/// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
///

View File

@ -38,7 +38,7 @@ pub fn check_for_loop_iter(
) -> bool {
if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent))
&& let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent)
&& let (clone_or_copy_needed, addr_of_exprs) = clone_or_copy_needed(cx, pat, body)
&& let (clone_or_copy_needed, references_to_binding) = clone_or_copy_needed(cx, pat, body)
&& !clone_or_copy_needed
&& let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
{
@ -123,14 +123,12 @@ pub fn check_for_loop_iter(
Applicability::MachineApplicable
};
diag.span_suggestion(expr.span, "use", snippet, applicability);
for addr_of_expr in addr_of_exprs {
match addr_of_expr.kind {
ExprKind::AddrOf(_, _, referent) => {
let span = addr_of_expr.span.with_hi(referent.span.lo());
diag.span_suggestion(span, "remove this `&`", "", applicability);
},
_ => unreachable!(),
}
if !references_to_binding.is_empty() {
diag.multipart_suggestion(
"remove any references to the binding",
references_to_binding,
applicability,
);
}
},
);

View File

@ -9,6 +9,7 @@ use rustc_lint::LateContext;
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, Ty};
use rustc_span::symbol::sym;
use rustc_span::Span;
pub(super) fn derefs_to_slice<'tcx>(
cx: &LateContext<'tcx>,
@ -96,15 +97,15 @@ pub(super) fn clone_or_copy_needed<'tcx>(
cx: &LateContext<'tcx>,
pat: &Pat<'tcx>,
body: &'tcx Expr<'tcx>,
) -> (bool, Vec<&'tcx Expr<'tcx>>) {
) -> (bool, Vec<(Span, String)>) {
let mut visitor = CloneOrCopyVisitor {
cx,
binding_hir_ids: pat_bindings(pat),
clone_or_copy_needed: false,
addr_of_exprs: Vec::new(),
references_to_binding: Vec::new(),
};
visitor.visit_expr(body);
(visitor.clone_or_copy_needed, visitor.addr_of_exprs)
(visitor.clone_or_copy_needed, visitor.references_to_binding)
}
/// Returns a vector of all `HirId`s bound by the pattern.
@ -127,7 +128,7 @@ struct CloneOrCopyVisitor<'cx, 'tcx> {
cx: &'cx LateContext<'tcx>,
binding_hir_ids: Vec<HirId>,
clone_or_copy_needed: bool,
addr_of_exprs: Vec<&'tcx Expr<'tcx>>,
references_to_binding: Vec<(Span, String)>,
}
impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> {
@ -142,8 +143,11 @@ impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> {
if self.is_binding(expr) {
if let Some(parent) = get_parent_expr(self.cx, expr) {
match parent.kind {
ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) => {
self.addr_of_exprs.push(parent);
ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, referent) => {
if !parent.span.from_expansion() {
self.references_to_binding
.push((parent.span.until(referent.span), String::new()));
}
return;
},
ExprKind::MethodCall(.., args, _) => {

View File

@ -12,14 +12,13 @@ use std::borrow::Cow;
declare_clippy_lint! {
/// ### What it does
/// Checks for idents which comprise of a single letter.
/// Checks for identifiers which consist of a single character (or fewer than the configured threshold).
///
/// Note: This lint can be very noisy when enabled; it may be desirable to only enable it
/// temporarily.
///
/// ### Why is this bad?
/// In many cases it's not, but at times it can severely hinder readability. Some codebases may
/// wish to disallow this to improve readability.
/// ### Why restrict this?
/// To improve readability by requiring that every variable has a name more specific than a single letter can be.
///
/// ### Example
/// ```rust,ignore

View File

@ -23,7 +23,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for structure field patterns bound to wildcards.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Using `..` instead is shorter and leaves the focus on
/// the fields that are actually bound.
///
@ -138,7 +138,7 @@ declare_clippy_lint! {
/// To enforce unseparated literal suffix style,
/// see the `separated_literal_suffix` lint.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Suffix style should be consistent.
///
/// ### Example
@ -166,7 +166,7 @@ declare_clippy_lint! {
/// To enforce separated literal suffix style,
/// see the `unseparated_literal_suffix` lint.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Suffix style should be consistent.
///
/// ### Example

View File

@ -10,7 +10,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks assertions without a custom panic message.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Without a good custom message, it'd be hard to understand what went wrong when the assertion fails.
/// A good custom message should be more about why the failure of the assertion is problematic
/// and not what is failed because the assertion already conveys that.

View File

@ -21,7 +21,7 @@ declare_clippy_lint! {
/// Checks for repeated slice indexing without asserting beforehand that the length
/// is greater than the largest index used to index into the slice.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// In the general case where the compiler does not have a lot of information
/// about the length of a slice, indexing it repeatedly will generate a bounds check
/// for every single index.

View File

@ -20,9 +20,9 @@ use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
/// Warns if there is missing doc for any private documentable item
/// Warns if there is missing documentation for any private documentable item.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Doc is good. *rustc* has a `MISSING_DOCS`
/// allowed-by-default lint for
/// public members, but has no way to enforce documentation of private items.

View File

@ -10,13 +10,16 @@ declare_clippy_lint! {
/// It lints if an exported function, method, trait method with default impl,
/// or trait method impl is not `#[inline]`.
///
/// ### Why is this bad?
/// In general, it is not. Functions can be inlined across
/// crates when that's profitable as long as any form of LTO is used. When LTO is disabled,
/// functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates
/// might intend for most of the methods in their public API to be able to be inlined across
/// crates even when LTO is disabled. For these types of crates, enabling this lint might make
/// sense. It allows the crate to require all exported methods to be `#[inline]` by default, and
/// ### Why restrict this?
/// When a function is not marked `#[inline]`, it is not
/// [a “small” candidate for automatic inlining][small], and LTO is not in use, then it is not
/// possible for the function to be inlined into the code of any crate other than the one in
/// which it is defined. Depending on the role of the function and the relationship of the crates,
/// this could significantly reduce performance.
///
/// Certain types of crates might intend for most of the methods in their public API to be able
/// to be inlined across crates even when LTO is disabled.
/// This lint allows those crates to require all exported methods to be `#[inline]` by default, and
/// then opt out for specific methods where this might not make sense.
///
/// ### Example
@ -51,6 +54,8 @@ declare_clippy_lint! {
/// fn def_bar() {} // missing #[inline]
/// }
/// ```
///
/// [small]: https://github.com/rust-lang/rust/pull/116505
#[clippy::version = "pre 1.29.0"]
pub MISSING_INLINE_IN_PUBLIC_ITEMS,
restriction,

View File

@ -10,16 +10,16 @@ use rustc_session::declare_lint_pass;
declare_clippy_lint! {
/// ### What it does
/// Checks if a provided method is used implicitly by a trait
/// implementation. A usage example would be a wrapper where every method
/// should perform some operation before delegating to the inner type's
/// implementation.
///
/// ### Why restrict this?
/// To ensure that a certain implementation implements every method; for example,
/// a wrapper type where every method should delegate to the corresponding method of
/// the inner type's implementation.
///
/// This lint should typically be enabled on a specific trait `impl` item
/// rather than globally.
///
/// ### Why is this bad?
/// Indicates that a method is missing.
///
/// ### Example
/// ```no_run
/// trait Trait {

View File

@ -12,9 +12,10 @@ declare_clippy_lint! {
/// whether the read occurs before or after the write depends on the evaluation
/// order of sub-expressions.
///
/// ### Why is this bad?
/// It is often confusing to read. As described [here](https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands),
/// the operands of these expressions are evaluated before applying the effects of the expression.
/// ### Why restrict this?
/// While [the evaluation order of sub-expressions] is fully specified in Rust,
/// it still may be confusing to read an expression where the evaluation order
/// affects its behavior.
///
/// ### Known problems
/// Code which intentionally depends on the evaluation
@ -40,6 +41,8 @@ declare_clippy_lint! {
/// };
/// let a = tmp + x;
/// ```
///
/// [order]: (https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands)
#[clippy::version = "pre 1.29.0"]
pub MIXED_READ_WRITE_IN_EXPRESSION,
restriction,

View File

@ -10,9 +10,9 @@ use std::path::{Component, Path};
declare_clippy_lint! {
/// ### What it does
/// Checks that module layout uses only self named module files, bans `mod.rs` files.
/// Checks that module layout uses only self named module files; bans `mod.rs` files.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Having multiple module layout styles in a project can be confusing.
///
/// ### Example
@ -41,7 +41,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks that module layout uses only `mod.rs` files.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Having multiple module layout styles in a project can be confusing.
///
/// ### Example

View File

@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::visitors::{for_each_expr_with_closures, Descend, Visitable};
use core::ops::ControlFlow::Continue;
use hir::def::{DefKind, Res};
use hir::{BlockCheckMode, ExprKind, Safety, QPath, UnOp};
use hir::{BlockCheckMode, ExprKind, QPath, Safety, UnOp};
use rustc_ast::Mutability;
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass};
@ -15,7 +15,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for `unsafe` blocks that contain more than one unsafe operation.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Combined with `undocumented_unsafe_blocks`,
/// this lint ensures that each unsafe operation must be independently justified.
/// Combined with `unused_unsafe`, this lint also ensures

View File

@ -14,7 +14,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `Mutex<X>` where an atomic will do.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Using a mutex just to make access to a plain bool or
/// reference sequential is shooting flies with cannons.
/// `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and

View File

@ -6,8 +6,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::{
higher, is_else_clause, is_expn_of, is_parent_stmt, peel_blocks, peel_blocks_with_stmt, span_extract_comment,
SpanlessEq,
higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, peel_blocks, peel_blocks_with_stmt,
span_extract_comment, SpanlessEq,
};
use rustc_ast::ast::LitKind;
use rustc_errors::Applicability;
@ -121,14 +121,7 @@ fn condition_needs_parentheses(e: &Expr<'_>) -> bool {
| ExprKind::Type(i, _)
| ExprKind::Index(i, _, _) = inner.kind
{
if matches!(
i.kind,
ExprKind::Block(..)
| ExprKind::ConstBlock(..)
| ExprKind::If(..)
| ExprKind::Loop(..)
| ExprKind::Match(..)
) {
if is_block_like(i) {
return true;
}
inner = i;

View File

@ -98,6 +98,10 @@ struct SimilarNamesLocalVisitor<'a, 'tcx> {
impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
fn check_single_char_names(&self) {
if self.single_char_names.last().map(Vec::len) == Some(0) {
return;
}
let num_single_char_names = self.single_char_names.iter().flatten().count();
let threshold = self.lint.single_char_binding_names_threshold;
if num_single_char_names as u64 > threshold {

View File

@ -70,7 +70,7 @@ declare_clippy_lint! {
/// Known safe built-in types like `Wrapping` or `Saturating`, floats, operations in constant
/// environments, allowed types and non-constant operations that won't overflow are ignored.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// For integers, overflow will trigger a panic in debug builds or wrap the result in
/// release mode; division by zero will cause a panic in either mode. As a result, it is
/// desirable to explicitly call checked, wrapping or saturating arithmetic methods.
@ -100,7 +100,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for float arithmetic.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// For some embedded systems or kernel development, it
/// can be useful to rule out floating-point numbers.
///
@ -502,7 +502,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for division of integers
///
/// ### Why is this bad?
/// ### Why restrict this?
/// When outside of some very specific algorithms,
/// integer division is very often a mistake because it discards the
/// remainder.
@ -596,7 +596,7 @@ declare_clippy_lint! {
/// value and constant, except in functions called `*eq*` (which probably
/// implement equality for a type involving floats).
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Floating point calculations are usually imprecise, so
/// asking if two values are *exactly* equal is asking for trouble. For a good
/// guide on what to do, see [the floating point
@ -653,8 +653,8 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for modulo arithmetic.
///
/// ### Why is this bad?
/// The results of modulo (%) operation might differ
/// ### Why restrict this?
/// The results of modulo (`%`) operation might differ
/// depending on the language, when negative numbers are involved.
/// If you interop with different languages it might be beneficial
/// to double check all places that use modulo arithmetic.

View File

@ -13,9 +13,9 @@ use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `panic!` or assertions in a function of type result.
/// Checks for usage of `panic!` or assertions in a function whose return type is `Result`.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided.
///
/// ### Known problems

View File

@ -14,8 +14,8 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `panic!`.
///
/// ### Why is this bad?
/// `panic!` will stop the execution of the executable.
/// ### Why restrict this?
/// This macro, or panics in general, may be unwanted in production code.
///
/// ### Example
/// ```no_run
@ -31,8 +31,8 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `unimplemented!`.
///
/// ### Why is this bad?
/// This macro should not be present in production code.
/// ### Why restrict this?
/// This macro, or panics in general, may be unwanted in production code.
///
/// ### Example
/// ```no_run
@ -48,9 +48,9 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `todo!`.
///
/// ### Why is this bad?
/// The `todo!` macro is often used for unfinished code, and it causes
/// code to panic. It should not be present in production code.
/// ### Why restrict this?
/// The `todo!` macro indicates the presence of unfinished code,
/// so it should not be present in production code.
///
/// ### Example
/// ```no_run
@ -70,8 +70,8 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `unreachable!`.
///
/// ### Why is this bad?
/// This macro can cause code to panic.
/// ### Why restrict this?
/// This macro, or panics in general, may be unwanted in production code.
///
/// ### Example
/// ```no_run

View File

@ -5,15 +5,16 @@ use rustc_session::declare_lint_pass;
declare_clippy_lint! {
/// ### What it does
/// Checks whether partial fields of a struct are public.
/// Checks whether some but not all fields of a `struct` are public.
///
/// Either make all fields of a type public, or make none of them public
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Most types should either be:
/// * Abstract data types: complex objects with opaque implementation which guard
/// interior invariants and expose intentionally limited API to the outside world.
/// * Data:relatively simple objects which group a bunch of related attributes together.
/// interior invariants and expose intentionally limited API to the outside world.
/// * Data:relatively simple objects which group a bunch of related attributes together,
/// but have no invariants.
///
/// ### Example
/// ```no_run

View File

@ -30,9 +30,8 @@ declare_clippy_lint! {
/// this lint can still be used to highlight areas of interest and ensure a good understanding
/// of ownership semantics.
///
/// ### Why is this bad?
/// It isn't bad in general. But in some contexts it can be desirable
/// because it increases ownership hints in the code, and will guard against some changes
/// ### Why restrict this?
/// It increases ownership hints in the code, and will guard against some changes
/// in ownership.
///
/// ### Example

View File

@ -5,13 +5,11 @@ use rustc_session::declare_lint_pass;
declare_clippy_lint! {
/// ### What it does
///
/// Restricts the usage of `pub use ...`
///
/// ### Why is this bad?
///
/// `pub use` is usually fine, but a project may wish to limit `pub use` instances to prevent
/// unintentional exports or to encourage placing exported items directly in public modules
/// ### Why restrict this?
/// A project may wish to limit `pub use` instances to prevent
/// unintentional exports, or to encourage placing exported items directly in public modules.
///
/// ### Example
/// ```no_run

View File

@ -9,7 +9,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for expressions that use the question mark operator and rejects them.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Sometimes code wants to avoid the question mark operator because for instance a local
/// block requires a macro to re-throw errors to attach additional information to the
/// error.

View File

@ -15,8 +15,10 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for raw string literals where a string literal can be used instead.
///
/// ### Why is this bad?
/// It's just unnecessary, but there are many cases where using a raw string literal is more
/// ### Why restrict this?
/// For consistent style by using simpler string literals whenever possible.
///
/// However, there are many cases where using a raw string literal is more
/// idiomatic than a string literal, so it's opt-in.
///
/// ### Example

View File

@ -46,7 +46,7 @@ declare_clippy_lint! {
/// Checks for slicing expressions which are equivalent to dereferencing the
/// value.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Some people may prefer to dereference rather than slice.
///
/// ### Example

View File

@ -11,7 +11,7 @@ declare_clippy_lint! {
/// ### What it does
/// Warns about needless / redundant type annotations.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Code without type annotations is shorter and in most cases
/// more idiomatic and easier to modify.
///

View File

@ -6,9 +6,11 @@ use rustc_session::declare_lint_pass;
declare_clippy_lint! {
/// ### What it does
/// Checks for usages of the `ref` keyword.
/// ### Why is this bad?
///
/// ### Why restrict this?
/// The `ref` keyword can be confusing for people unfamiliar with it, and often
/// it is more concise to use `&` instead.
///
/// ### Example
/// ```no_run
/// let opt = Some(5);

View File

@ -3,8 +3,8 @@ use clippy_utils::source::{snippet_opt, snippet_with_context};
use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
use clippy_utils::{
fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res, path_to_local_id, span_contains_cfg,
span_find_starting_semi,
binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res,
path_to_local_id, span_contains_cfg, span_find_starting_semi,
};
use core::ops::ControlFlow;
use rustc_errors::Applicability;
@ -129,7 +129,7 @@ enum RetReplacement<'tcx> {
Empty,
Block,
Unit,
IfSequence(Cow<'tcx, str>, Applicability),
NeedsPar(Cow<'tcx, str>, Applicability),
Expr(Cow<'tcx, str>, Applicability),
}
@ -139,13 +139,13 @@ impl<'tcx> RetReplacement<'tcx> {
Self::Empty | Self::Expr(..) => "remove `return`",
Self::Block => "replace `return` with an empty block",
Self::Unit => "replace `return` with a unit value",
Self::IfSequence(..) => "remove `return` and wrap the sequence with parentheses",
Self::NeedsPar(..) => "remove `return` and wrap the sequence with parentheses",
}
}
fn applicability(&self) -> Applicability {
match self {
Self::Expr(_, ap) | Self::IfSequence(_, ap) => *ap,
Self::Expr(_, ap) | Self::NeedsPar(_, ap) => *ap,
_ => Applicability::MachineApplicable,
}
}
@ -157,7 +157,7 @@ impl<'tcx> Display for RetReplacement<'tcx> {
Self::Empty => write!(f, ""),
Self::Block => write!(f, "{{}}"),
Self::Unit => write!(f, "()"),
Self::IfSequence(inner, _) => write!(f, "({inner})"),
Self::NeedsPar(inner, _) => write!(f, "({inner})"),
Self::Expr(inner, _) => write!(f, "{inner}"),
}
}
@ -244,7 +244,11 @@ impl<'tcx> LateLintPass<'tcx> for Return {
err.span_label(local.span, "unnecessary `let` binding");
if let Some(mut snippet) = snippet_opt(cx, initexpr.span) {
if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
if binary_expr_needs_parentheses(initexpr) {
if !has_enclosing_paren(&snippet) {
snippet = format!("({snippet})");
}
} else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
if !has_enclosing_paren(&snippet) {
snippet = format!("({snippet})");
}
@ -349,8 +353,8 @@ fn check_final_expr<'tcx>(
let mut applicability = Applicability::MachineApplicable;
let (snippet, _) = snippet_with_context(cx, inner_expr.span, ret_span.ctxt(), "..", &mut applicability);
if expr_contains_conjunctive_ifs(inner_expr) {
RetReplacement::IfSequence(snippet, applicability)
if binary_expr_needs_parentheses(inner_expr) {
RetReplacement::NeedsPar(snippet, applicability)
} else {
RetReplacement::Expr(snippet, applicability)
}
@ -404,18 +408,6 @@ fn check_final_expr<'tcx>(
}
}
fn expr_contains_conjunctive_ifs<'tcx>(expr: &'tcx Expr<'tcx>) -> bool {
fn contains_if(expr: &Expr<'_>, on_if: bool) -> bool {
match expr.kind {
ExprKind::If(..) => on_if,
ExprKind::Binary(_, left, right) => contains_if(left, true) || contains_if(right, true),
_ => false,
}
}
contains_if(expr, false)
}
fn emit_return_lint(
cx: &LateContext<'_>,
ret_span: Span,

View File

@ -14,7 +14,7 @@ declare_clippy_lint! {
/// It lints if a struct has two methods with the same name:
/// one from a trait, another not from trait.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Confusing.
///
/// ### Example

View File

@ -11,8 +11,7 @@ declare_clippy_lint! {
/// Suggests moving the semicolon after a block to the inside of the block, after its last
/// expression.
///
/// ### Why is this bad?
///
/// ### Why restrict this?
/// For consistency it's best to have the semicolon inside/outside the block. Either way is fine
/// and this lint suggests inside the block.
/// Take a look at `semicolon_outside_block` for the other alternative.
@ -40,8 +39,7 @@ declare_clippy_lint! {
///
/// Suggests moving the semicolon from a block's final expression outside of the block.
///
/// ### Why is this bad?
///
/// ### Why restrict this?
/// For consistency it's best to have the semicolon inside/outside the block. Either way is fine
/// and this lint suggests outside the block.
/// Take a look at `semicolon_inside_block` for the other alternative.

View File

@ -15,10 +15,10 @@ declare_clippy_lint! {
/// Checks for bindings that shadow other bindings already in
/// scope, while just changing reference level or mutability.
///
/// ### Why is this bad?
/// Not much, in fact it's a very common pattern in Rust
/// code. Still, some may opt to avoid it in their code base, they can set this
/// lint to `Warn`.
/// ### Why restrict this?
/// To require that what are formally distinct variables be given distinct names.
///
/// See also `shadow_reuse` and `shadow_unrelated` for other restrictions on shadowing.
///
/// ### Example
/// ```no_run
@ -42,12 +42,13 @@ declare_clippy_lint! {
/// Checks for bindings that shadow other bindings already in
/// scope, while reusing the original value.
///
/// ### Why is this bad?
/// Not too much, in fact it's a common pattern in Rust
/// code. Still, some argue that name shadowing like this hurts readability,
/// ### Why restrict this?
/// Some argue that name shadowing like this hurts readability,
/// because a value may be bound to different things depending on position in
/// the code.
///
/// See also `shadow_same` and `shadow_unrelated` for other restrictions on shadowing.
///
/// ### Example
/// ```no_run
/// let x = 2;
@ -70,11 +71,14 @@ declare_clippy_lint! {
/// scope, either without an initialization or with one that does not even use
/// the original value.
///
/// ### Why is this bad?
/// Name shadowing can hurt readability, especially in
/// ### Why restrict this?
/// Shadowing a binding with a closely related one is part of idiomatic Rust,
/// but shadowing a binding by accident with an unrelated one may indicate a mistake.
///
/// Additionally, name shadowing in general can hurt readability, especially in
/// large code bases, because it is easy to lose track of the active binding at
/// any place in the code. This can be alleviated by either giving more specific
/// names to bindings or introducing more scopes to contain the bindings.
/// any place in the code. If linting against all shadowing is desired, you may wish
/// to use the `shadow_same` and `shadow_reuse` lints as well.
///
/// ### Example
/// ```no_run

View File

@ -13,13 +13,20 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for functions that are only used once. Does not lint tests.
///
/// ### Why is this bad?
/// It's usually not, splitting a function into multiple parts often improves readability and in
/// the case of generics, can prevent the compiler from duplicating the function dozens of
/// time; instead, only duplicating a thunk. But this can prevent segmentation across a
/// codebase, where many small functions are used only once.
/// ### Why restrict this?
/// If a function is only used once (perhaps because it used to be used more widely),
/// then the code could be simplified by moving that function's code into its caller.
///
/// Note: If this lint is used, prepare to allow this a lot.
/// However, there are reasons not to do this everywhere:
///
/// * Splitting a large function into multiple parts often improves readability
/// by giving names to its parts.
/// * A functions signature might serve a necessary purpose, such as constraining
/// the type of a closure passed to it.
/// * Generic functions might call non-generic functions to reduce duplication
/// in the produced machine code.
///
/// If this lint is used, prepare to `#[allow]` it a lot.
///
/// ### Example
/// ```no_run

View File

@ -9,11 +9,10 @@ declare_clippy_lint! {
/// Checks for lifetimes with names which are one character
/// long.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// A single character is likely not enough to express the
/// purpose of a lifetime. Using a longer name can make code
/// easier to understand, especially for those who are new to
/// Rust.
/// easier to understand.
///
/// ### Known problems
/// Rust programmers and learning resources tend to use single

View File

@ -12,11 +12,9 @@ use rustc_span::{sym, Span};
declare_clippy_lint! {
/// ### What it does
///
/// Finds items imported through `std` when available through `core`.
///
/// ### Why is this bad?
///
/// ### Why restrict this?
/// Crates which have `no_std` compatibility may wish to ensure types are imported from core to ensure
/// disabling `std` does not cause the crate to fail to compile. This lint is also useful for crates
/// migrating to become `no_std` compatible.
@ -37,11 +35,9 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
///
/// Finds items imported through `std` when available through `alloc`.
///
/// ### Why is this bad?
///
/// ### Why restrict this?
/// Crates which have `no_std` compatibility and require alloc may wish to ensure types are imported from
/// alloc to ensure disabling `std` does not cause the crate to fail to compile. This lint is also useful
/// for crates migrating to become `no_std` compatible.
@ -63,11 +59,9 @@ declare_clippy_lint! {
declare_clippy_lint! {
/// ### What it does
///
/// Finds items imported through `alloc` when available through `core`.
///
/// ### Why is this bad?
///
/// ### Why restrict this?
/// Crates which have `no_std` compatibility and may optionally require alloc may wish to ensure types are
/// imported from core to ensure disabling `alloc` does not cause the crate to fail to compile. This lint
/// is also useful for crates migrating to become `no_std` compatible.

View File

@ -45,10 +45,10 @@ declare_clippy_lint! {
/// `String`, but only if [`string_add_assign`](#string_add_assign) does *not*
/// match.
///
/// ### Why is this bad?
/// It's not bad in and of itself. However, this particular
/// ### Why restrict this?
/// This particular
/// `Add` implementation is asymmetric (the other operand need not be `String`,
/// but `x` does), while addition as mathematically defined is symmetric, also
/// but `x` does), while addition as mathematically defined is symmetric, and
/// the `String::push_str(_)` function is a perfectly good replacement.
/// Therefore, some dislike it and wish not to have it in their code.
///
@ -123,7 +123,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for slice operations on strings
///
/// ### Why is this bad?
/// ### Why restrict this?
/// UTF-8 characters span multiple bytes, and it is easy to inadvertently confuse character
/// counts and string indices. This may lead to panics, and should warrant some test cases
/// containing wide UTF-8 characters. This lint is most useful in code that should avoid
@ -364,10 +364,10 @@ declare_clippy_lint! {
/// ### What it does
/// This lint checks for `.to_string()` method calls on values of type `&str`.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// The `to_string` method is also used on other types to convert them to a string.
/// When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better
/// expressed with `.to_owned()`.
/// When called on a `&str` it turns the `&str` into the owned variant `String`, which can be
/// more specifically expressed with `.to_owned()`.
///
/// ### Example
/// ```no_run
@ -415,9 +415,10 @@ declare_clippy_lint! {
/// ### What it does
/// This lint checks for `.to_string()` method calls on values of type `String`.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// The `to_string` method is also used on other types to convert them to a string.
/// When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`.
/// When called on a `String` it only clones the `String`, which can be more specifically
/// expressed with `.clone()`.
///
/// ### Example
/// ```no_run

View File

@ -11,8 +11,10 @@ use rustc_session::declare_lint_pass;
declare_clippy_lint! {
/// ### What it does
/// Warns for a Bitwise XOR (`^`) operator being probably confused as a powering. It will not trigger if any of the numbers are not in decimal.
/// ### Why is this bad?
///
/// ### Why restrict this?
/// It's most probably a typo and may lead to unexpected behaviours.
///
/// ### Example
/// ```no_run
/// let x = 3_i32 ^ 4_i32;

View File

@ -11,9 +11,11 @@ declare_clippy_lint! {
/// ### What it does
/// Triggers when a testing function (marked with the `#[test]` attribute) isn't inside a testing module
/// (marked with `#[cfg(test)]`).
/// ### Why is this bad?
///
/// ### Why restrict this?
/// The idiomatic (and more performant) way of writing tests is inside a testing module (flagged with `#[cfg(test)]`),
/// having test functions outside of this module is confusing and may lead to them being "hidden".
///
/// ### Example
/// ```no_run
/// #[test]

View File

@ -217,7 +217,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Expressions such as `Rc<String>` usually have no advantage over `Rc<str>`, since
/// it is larger and involves an extra level of indirection, and doesn't implement `Borrow<str>`.
///
@ -274,7 +274,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for `Rc<Mutex<T>>`.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// `Rc` is used in single thread and `Mutex` is used in multi thread.
/// Consider using `Rc<RefCell<T>>` in single thread or `Arc<Mutex<T>>` in multi thread.
///

View File

@ -38,10 +38,9 @@ declare_clippy_lint! {
/// );
/// ```
///
/// ### Why is this bad?
/// Undocumented unsafe blocks and impls can make it difficult to
/// read and maintain code, as well as uncover unsoundness
/// and bugs.
/// ### Why restrict this?
/// Undocumented unsafe blocks and impls can make it difficult to read and maintain code.
/// Writing out the safety justification may help in discovering unsoundness or bugs.
///
/// ### Example
/// ```no_run
@ -67,7 +66,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for `// SAFETY: ` comments on safe code.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Safe code has no safety requirements, so there is no need to
/// describe safety invariants.
///

View File

@ -31,7 +31,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for non-ASCII characters in string and char literals.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Yeah, we know, the 90's called and wanted their charset
/// back. Even so, there still are editors and other programs out there that
/// don't work well with Unicode. So if the code is meant to be used

View File

@ -9,7 +9,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for imports ending in `::{self}`.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// In most cases, this can be written much more cleanly by omitting `::{self}`.
///
/// ### Known problems

View File

@ -13,8 +13,10 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for functions of type `Result` that contain `expect()` or `unwrap()`
///
/// ### Why is this bad?
/// These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics.
/// ### Why restrict this?
/// These functions promote recoverable errors to non-recoverable errors,
/// which may be undesirable in code bases which wish to avoid panics,
/// or be a bug in the specific function.
///
/// ### Known problems
/// This can cause false positives in functions that handle both recoverable and non recoverable errors.

View File

@ -32,7 +32,7 @@ declare_clippy_lint! {
/// ### What it does
/// Checks for usage of `pub(<loc>)` with `in`.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Consistency. Use it or don't, just be consistent about it.
///
/// Also see the `pub_without_shorthand` lint for an alternative.
@ -57,7 +57,7 @@ declare_clippy_lint! {
/// Note: As you cannot write a module's path in `pub(<loc>)`, this will only trigger on
/// `pub(super)` and the like.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// Consistency. Use it or don't, just be consistent about it.
///
/// Also see the `pub_with_shorthand` lint for an alternative.

View File

@ -66,7 +66,7 @@ declare_clippy_lint! {
/// Checks for printing on *stdout*. The purpose of this lint
/// is to catch debugging remnants.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// People often print on *stdout* while debugging an
/// application and might forget to remove those prints afterward.
///
@ -88,7 +88,7 @@ declare_clippy_lint! {
/// Checks for printing on *stderr*. The purpose of this lint
/// is to catch debugging remnants.
///
/// ### Why is this bad?
/// ### Why restrict this?
/// People often print on *stderr* while debugging an
/// application and might forget to remove those prints afterward.
///
@ -110,15 +110,18 @@ declare_clippy_lint! {
/// Checks for usage of `Debug` formatting. The purpose of this
/// lint is to catch debugging remnants.
///
/// ### Why is this bad?
/// The purpose of the `Debug` trait is to facilitate
/// debugging Rust code. It should not be used in user-facing output.
/// ### Why restrict this?
/// The purpose of the `Debug` trait is to facilitate debugging Rust code,
/// and [no guarantees are made about its output][stability].
/// It should not be used in user-facing output.
///
/// ### Example
/// ```no_run
/// # let foo = "bar";
/// println!("{:?}", foo);
/// ```
///
/// [stability]: https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html#stability
#[clippy::version = "pre 1.29.0"]
pub USE_DEBUG,
restriction,

View File

@ -412,8 +412,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
/// Simple constant folding: Insert an expression, get a constant or none.
pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
match e.kind {
ExprKind::ConstBlock(e) |
ExprKind::DropTemps(e) => self.expr(e),
ExprKind::ConstBlock(e) | ExprKind::DropTemps(e) => self.expr(e),
ExprKind::Path(ref qpath) => {
self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| {
let result = mir_to_const(this.lcx, result)?;
@ -491,8 +490,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
/// leaves the local crate.
pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option<bool> {
match e.kind {
ExprKind::ConstBlock(e) |
ExprKind::DropTemps(e) => self.expr_is_empty(e),
ExprKind::ConstBlock(e) | ExprKind::DropTemps(e) => self.expr_is_empty(e),
ExprKind::Path(ref qpath) => {
if !self
.typeck_results

View File

@ -769,7 +769,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
// closures inherit TypeckResults
self.hash_expr(self.cx.tcx.hir().body(body).value);
},
ExprKind::ConstBlock(ref l_id) => {
ExprKind::ConstBlock(l_id) => {
self.hash_expr(l_id);
},
ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => {

View File

@ -3370,3 +3370,25 @@ pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
Node::Stmt(..) | Node::Block(Block { stmts: &[], .. })
)
}
/// Returns true if the given `expr` is a block or resembled as a block,
/// such as `if`, `loop`, `match` expressions etc.
pub fn is_block_like(expr: &Expr<'_>) -> bool {
matches!(
expr.kind,
ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
)
}
/// Returns true if the given `expr` is binary expression that needs to be wrapped in parentheses.
pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
match expr.kind {
ExprKind::Binary(_, lhs, _) => contains_block(lhs, true),
_ if is_block_like(expr) => is_operand,
_ => false,
}
}
contains_block(expr, false)
}

View File

@ -9,7 +9,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, FnDecl, Safety, LangItem, TyKind};
use rustc_hir::{Expr, FnDecl, LangItem, Safety, TyKind};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::LateContext;
use rustc_middle::mir::interpret::Scalar;
@ -18,8 +18,8 @@ use rustc_middle::traits::EvaluationResult;
use rustc_middle::ty::layout::ValidityRequirement;
use rustc_middle::ty::{
self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, Upcast, TraitRef, Ty, TyCtxt,
TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr,
GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
};
use rustc_span::symbol::Ident;
use rustc_span::{sym, Span, Symbol, DUMMY_SP};

View File

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2024-05-16"
channel = "nightly-2024-05-30"
components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]

View File

@ -11,11 +11,15 @@ use rustc_lint::{Lint, LintContext};
use rustc_middle::ty::TyCtxt;
pub fn a(cx: impl LintContext, lint: &'static Lint, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
cx.span_lint(lint, span, |lint| { lint.primary_message(msg); });
cx.span_lint(lint, span, |lint| {
lint.primary_message(msg);
});
}
pub fn b(tcx: TyCtxt<'_>, lint: &'static Lint, hir_id: HirId, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
tcx.node_span_lint(lint, hir_id, span, |lint| { lint.primary_message(msg); });
tcx.node_span_lint(lint, hir_id, span, |lint| {
lint.primary_message(msg);
});
}
fn main() {}

View File

@ -1,18 +1,22 @@
error: use of a disallowed method `rustc_lint::context::LintContext::span_lint`
--> tests/ui-internal/disallow_span_lint.rs:14:5
|
LL | cx.span_lint(lint, span, |lint| { lint.primary_message(msg); });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | / cx.span_lint(lint, span, |lint| {
LL | | lint.primary_message(msg);
LL | | });
| |______^
|
= note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead (from clippy.toml)
= note: `-D clippy::disallowed-methods` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]`
error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint`
--> tests/ui-internal/disallow_span_lint.rs:18:5
--> tests/ui-internal/disallow_span_lint.rs:20:5
|
LL | tcx.node_span_lint(lint, hir_id, span, |lint| { lint.primary_message(msg); });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
LL | / tcx.node_span_lint(lint, hir_id, span, |lint| {
LL | | lint.primary_message(msg);
LL | | });
| |______^
|
= note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead (from clippy.toml)

View File

@ -1,3 +1,4 @@
#![feature(lint_reasons)]
#![allow(unused)]
#![warn(clippy::derive_partial_eq_without_eq)]
@ -14,6 +15,22 @@ pub struct MissingEq {
bar: String,
}
// Check that we honor the `allow` attribute
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Debug, PartialEq)]
pub struct AllowedMissingEq {
foo: u32,
bar: String,
}
// Check that we honor the `expect` attribute
#[expect(clippy::derive_partial_eq_without_eq)]
#[derive(Debug, PartialEq)]
pub struct ExpectedMissingEq {
foo: u32,
bar: String,
}
// Eq is derived
#[derive(PartialEq, Eq)]
pub struct NotMissingEq {

View File

@ -1,3 +1,4 @@
#![feature(lint_reasons)]
#![allow(unused)]
#![warn(clippy::derive_partial_eq_without_eq)]
@ -14,6 +15,22 @@ pub struct MissingEq {
bar: String,
}
// Check that we honor the `allow` attribute
#[allow(clippy::derive_partial_eq_without_eq)]
#[derive(Debug, PartialEq)]
pub struct AllowedMissingEq {
foo: u32,
bar: String,
}
// Check that we honor the `expect` attribute
#[expect(clippy::derive_partial_eq_without_eq)]
#[derive(Debug, PartialEq)]
pub struct ExpectedMissingEq {
foo: u32,
bar: String,
}
// Eq is derived
#[derive(PartialEq, Eq)]
pub struct NotMissingEq {

View File

@ -1,5 +1,5 @@
error: you are deriving `PartialEq` and can implement `Eq`
--> tests/ui/derive_partial_eq_without_eq.rs:11:17
--> tests/ui/derive_partial_eq_without_eq.rs:12:17
|
LL | #[derive(Debug, PartialEq)]
| ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
@ -8,73 +8,73 @@ LL | #[derive(Debug, PartialEq)]
= help: to override `-D warnings` add `#[allow(clippy::derive_partial_eq_without_eq)]`
error: you are deriving `PartialEq` and can implement `Eq`
--> tests/ui/derive_partial_eq_without_eq.rs:53:10
--> tests/ui/derive_partial_eq_without_eq.rs:70:10
|
LL | #[derive(PartialEq)]
| ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
error: you are deriving `PartialEq` and can implement `Eq`
--> tests/ui/derive_partial_eq_without_eq.rs:59:10
--> tests/ui/derive_partial_eq_without_eq.rs:76:10
|
LL | #[derive(PartialEq)]
| ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
error: you are deriving `PartialEq` and can implement `Eq`
--> tests/ui/derive_partial_eq_without_eq.rs:65:10
--> tests/ui/derive_partial_eq_without_eq.rs:82:10
|
LL | #[derive(PartialEq)]
| ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
error: you are deriving `PartialEq` and can implement `Eq`
--> tests/ui/derive_partial_eq_without_eq.rs:68:10
--> tests/ui/derive_partial_eq_without_eq.rs:85:10
|
LL | #[derive(PartialEq)]
| ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
error: you are deriving `PartialEq` and can implement `Eq`
--> tests/ui/derive_partial_eq_without_eq.rs:74:10
--> tests/ui/derive_partial_eq_without_eq.rs:91:10
|
LL | #[derive(PartialEq)]
| ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
error: you are deriving `PartialEq` and can implement `Eq`
--> tests/ui/derive_partial_eq_without_eq.rs:80:10
--> tests/ui/derive_partial_eq_without_eq.rs:97:10
|
LL | #[derive(PartialEq)]
| ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
error: you are deriving `PartialEq` and can implement `Eq`
--> tests/ui/derive_partial_eq_without_eq.rs:93:17
--> tests/ui/derive_partial_eq_without_eq.rs:110:17
|
LL | #[derive(Debug, PartialEq, Clone)]
| ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
error: you are deriving `PartialEq` and can implement `Eq`
--> tests/ui/derive_partial_eq_without_eq.rs:96:10
--> tests/ui/derive_partial_eq_without_eq.rs:113:10
|
LL | #[derive(PartialEq)]
| ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
error: you are deriving `PartialEq` and can implement `Eq`
--> tests/ui/derive_partial_eq_without_eq.rs:103:14
--> tests/ui/derive_partial_eq_without_eq.rs:120:14
|
LL | #[derive(PartialEq)]
| ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
error: you are deriving `PartialEq` and can implement `Eq`
--> tests/ui/derive_partial_eq_without_eq.rs:106:14
--> tests/ui/derive_partial_eq_without_eq.rs:123:14
|
LL | #[derive(PartialEq)]
| ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
error: you are deriving `PartialEq` and can implement `Eq`
--> tests/ui/derive_partial_eq_without_eq.rs:166:14
--> tests/ui/derive_partial_eq_without_eq.rs:183:14
|
LL | #[derive(PartialEq)]
| ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
error: you are deriving `PartialEq` and can implement `Eq`
--> tests/ui/derive_partial_eq_without_eq.rs:174:14
--> tests/ui/derive_partial_eq_without_eq.rs:191:14
|
LL | #[derive(PartialEq)]
| ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`

Some files were not shown because too many files have changed in this diff Show More