mirror of
https://github.com/rust-lang/rust-clippy
synced 2024-11-10 07:04:18 +00:00
Merge commit 'c9139bd546d9cd69df817faeab62c5f9b1a51337' into clippy-subtree-update
This commit is contained in:
parent
d38920f677
commit
f67f72695a
128 changed files with 1430 additions and 684 deletions
|
@ -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)]
|
||||
|
|
|
@ -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].
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
///
|
||||
|
|
|
@ -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 isn’t.
|
||||
///
|
||||
/// ### 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]);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 can’t 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 type’s 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,
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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 target’s 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 target’s 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 target’s endianness rather than big endian.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```rust,ignore
|
||||
|
|
|
@ -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`
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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`.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>()
|
||||
);
|
||||
|
|
|
@ -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).
|
||||
///
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(_)
|
||||
|
|
|
@ -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)
|
||||
///
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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, _) => {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 function’s 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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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.
|
||||
///
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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, _) => {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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"]
|
||||
|
|
|
@ -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() {}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue