Merge commit 'edb720b199' into clippyup

This commit is contained in:
Philipp Krones 2023-11-16 19:13:24 +01:00
parent 9aa2330e41
commit 6246f0446a
326 changed files with 10349 additions and 10380 deletions

View file

@ -6,11 +6,70 @@ document.
## Unreleased / Beta / In Rust Nightly ## Unreleased / Beta / In Rust Nightly
[1e8fdf49...master](https://github.com/rust-lang/rust-clippy/compare/1e8fdf49...master) [7671c283...master](https://github.com/rust-lang/rust-clippy/compare/7671c283...master)
## Rust 1.74
Current stable, released 2023-11-16
[View all 94 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-08-11T15%3A29%3A18Z..2023-09-25T08%3A48%3A22Z+base%3Amaster)
### New Lints
* [`redundant_as_str`]
[#11526](https://github.com/rust-lang/rust-clippy/pull/11526)
* [`needless_borrows_for_generic_args`]
[#11511](https://github.com/rust-lang/rust-clippy/pull/11511)
* [`path_ends_with_ext`]
[#11483](https://github.com/rust-lang/rust-clippy/pull/11483)
* [`unnecessary_map_on_constructor`]
[#11413](https://github.com/rust-lang/rust-clippy/pull/11413)
* [`missing_asserts_for_indexing`]
[#10692](https://github.com/rust-lang/rust-clippy/pull/10692)
* [`iter_out_of_bounds`]
[#11396](https://github.com/rust-lang/rust-clippy/pull/11396)
* [`implied_bounds_in_impls`]
[#11362](https://github.com/rust-lang/rust-clippy/pull/11362)
* [`reserve_after_initialization`]
[#11373](https://github.com/rust-lang/rust-clippy/pull/11373)
* [`should_panic_without_expect`]
[#11204](https://github.com/rust-lang/rust-clippy/pull/11204)
### Moves and Deprecations
* Renamed `incorrect_clone_impl_on_copy_type` to [`non_canonical_clone_impl`]
[#11358](https://github.com/rust-lang/rust-clippy/pull/11358)
* Renamed `incorrect_partial_ord_impl_on_ord_type` to [`non_canonical_partial_ord_impl`]
[#11358](https://github.com/rust-lang/rust-clippy/pull/11358)
* Moved [`non_canonical_clone_impl`] to `suspicious` (Now warn-by-default)
[#11358](https://github.com/rust-lang/rust-clippy/pull/11358)
* Moved [`non_canonical_partial_ord_impl`] to `suspicious` (Now warn-by-default)
[#11358](https://github.com/rust-lang/rust-clippy/pull/11358)
* Moved [`needless_pass_by_ref_mut`] to `nursery` (Now allow-by-default)
[#11596](https://github.com/rust-lang/rust-clippy/pull/11596)
### Enhancements
* [`undocumented_unsafe_blocks`]: The config values [`accept-comment-above-statement`] and
[`accept-comment-above-attributes`] to `true` by default
[#11170](https://github.com/rust-lang/rust-clippy/pull/11170)
* [`explicit_iter_loop`]: Added [`enforce-iter-loop-reborrow`] to disable reborrow linting by default
[#11418](https://github.com/rust-lang/rust-clippy/pull/11418)
### ICE Fixes
* [`enum_variant_names`]: No longer crashes if the threshold is 0 and the enum has no variants
[#11552](https://github.com/rust-lang/rust-clippy/pull/11552)
* [`cast_possible_truncation`]: No longer crashes on values larger than `u64::MAX`
[#11517](https://github.com/rust-lang/rust-clippy/pull/11517)
* [`tuple_array_conversions`]: No longer crashes if the array length is not usize
[#11379](https://github.com/rust-lang/rust-clippy/pull/11379)
* [`useless_conversion`]: No longer crashes, when the receiver is a non-fn item
[#11070](https://github.com/rust-lang/rust-clippy/pull/11070)
## Rust 1.73 ## Rust 1.73
Current stable, released 2023-10-05 Released 2023-10-05
[View all 103 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-07-02T12%3A24%3A40Z..2023-08-11T11%3A09%3A56Z+base%3Amaster) [View all 103 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-07-02T12%3A24%3A40Z..2023-08-11T11%3A09%3A56Z+base%3Amaster)
@ -5123,6 +5182,7 @@ Released 2018-09-13
[`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections [`iter_on_empty_collections`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_empty_collections
[`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items [`iter_on_single_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_on_single_items
[`iter_out_of_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_out_of_bounds [`iter_out_of_bounds`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_out_of_bounds
[`iter_over_hash_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_over_hash_type
[`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned [`iter_overeager_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_overeager_cloned
[`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next [`iter_skip_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_next
[`iter_skip_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_zero [`iter_skip_zero`]: https://rust-lang.github.io/rust-clippy/master/index.html#iter_skip_zero

View file

@ -146,16 +146,10 @@ For example, the [`else_if_without_else`][else_if_without_else] lint is register
pub mod else_if_without_else; pub mod else_if_without_else;
// ... // ...
pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &Conf) { pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
// ... // ...
store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse)); store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse));
// ... // ...
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
// ...
LintId::of(&else_if_without_else::ELSE_IF_WITHOUT_ELSE),
// ...
]);
} }
``` ```

View file

@ -1,6 +1,6 @@
[package] [package]
name = "clippy" name = "clippy"
version = "0.1.75" version = "0.1.76"
description = "A bunch of helpful lints to avoid common pitfalls in Rust" description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy" repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md" readme = "README.md"

View file

@ -270,7 +270,7 @@ When using `cargo dev new_lint`, the lint is automatically registered and
nothing more has to be done. nothing more has to be done.
When declaring a new lint by hand and `cargo dev update_lints` is used, the lint When declaring a new lint by hand and `cargo dev update_lints` is used, the lint
pass may have to be registered manually in the `register_plugins` function in pass may have to be registered manually in the `register_lints` function in
`clippy_lints/src/lib.rs`: `clippy_lints/src/lib.rs`:
```rust,ignore ```rust,ignore
@ -436,7 +436,7 @@ need to ensure that the MSRV configured for the project is >= the MSRV of the
required Rust feature. If multiple features are required, just use the one with required Rust feature. If multiple features are required, just use the one with
a lower MSRV. a lower MSRV.
First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`]. First, add an MSRV alias for the required feature in [`clippy_config::msrvs`].
This can be accessed later as `msrvs::STR_STRIP_PREFIX`, for example. This can be accessed later as `msrvs::STR_STRIP_PREFIX`, for example.
```rust ```rust
@ -506,7 +506,7 @@ fn msrv_1_45() {
``` ```
As a last step, the lint should be added to the lint documentation. This is done As a last step, the lint should be added to the lint documentation. This is done
in `clippy_lints/src/utils/conf.rs`: in `clippy_config/src/conf.rs`:
```rust ```rust
define_Conf! { define_Conf! {
@ -516,7 +516,7 @@ define_Conf! {
} }
``` ```
[`clippy_utils::msrvs`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_utils/msrvs/index.html [`clippy_config::msrvs`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_config/msrvs/index.html
## Author lint ## Author lint
@ -657,7 +657,7 @@ Adding a configuration to a lint can be useful for
thresholds or to constrain some behavior that can be seen as a false positive thresholds or to constrain some behavior that can be seen as a false positive
for some users. Adding a configuration is done in the following steps: for some users. Adding a configuration is done in the following steps:
1. Adding a new configuration entry to [`clippy_lints::utils::conf`] like this: 1. Adding a new configuration entry to [`clippy_config::conf`] like this:
```rust,ignore ```rust,ignore
/// Lint: LINT_NAME. /// Lint: LINT_NAME.
@ -736,7 +736,7 @@ for some users. Adding a configuration is done in the following steps:
Run `cargo collect-metadata` to generate documentation changes for the book. Run `cargo collect-metadata` to generate documentation changes for the book.
[`clippy_lints::utils::conf`]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/utils/conf.rs [`clippy_config::conf`]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_config/src/conf.rs
[`clippy_lints` lib file]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs [`clippy_lints` lib file]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs
[`tests/ui`]: https://github.com/rust-lang/rust-clippy/blob/master/tests/ui [`tests/ui`]: https://github.com/rust-lang/rust-clippy/blob/master/tests/ui
[`tests/ui-toml`]: https://github.com/rust-lang/rust-clippy/blob/master/tests/ui-toml [`tests/ui-toml`]: https://github.com/rust-lang/rust-clippy/blob/master/tests/ui-toml

View file

@ -186,7 +186,7 @@ However, sometimes we might want to declare a new lint by hand. In this case,
we'd use `cargo dev update_lints` command afterwards. we'd use `cargo dev update_lints` command afterwards.
When a lint is manually declared, we might need to register the lint pass When a lint is manually declared, we might need to register the lint pass
manually in the `register_plugins` function in `clippy_lints/src/lib.rs`: manually in the `register_lints` function in `clippy_lints/src/lib.rs`:
```rust ```rust
store.register_late_pass(|_| Box::new(foo_functions::FooFunctions)); store.register_late_pass(|_| Box::new(foo_functions::FooFunctions));

View file

@ -1 +1,7 @@
avoid-breaking-exported-api = false avoid-breaking-exported-api = false
# use the various `span_lint_*` methods instead, which also add a link to the docs
disallowed-methods = [
"rustc_lint::context::LintContext::struct_span_lint",
"rustc_middle::ty::context::TyCtxt::struct_span_lint_hir"
]

View file

@ -1,6 +1,6 @@
[package] [package]
name = "clippy_config" name = "clippy_config"
version = "0.1.75" version = "0.1.76"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View file

@ -1,7 +1,6 @@
use serde::de::{self, Deserializer, Visitor}; use serde::de::{self, Deserializer, Visitor};
use serde::{ser, Deserialize, Serialize}; use serde::{ser, Deserialize, Serialize};
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher};
#[derive(Clone, Debug, Deserialize)] #[derive(Clone, Debug, Deserialize)]
pub struct Rename { pub struct Rename {
@ -33,32 +32,19 @@ impl DisallowedPath {
} }
} }
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub enum MatchLintBehaviour { pub enum MatchLintBehaviour {
AllTypes, AllTypes,
WellKnownTypes, WellKnownTypes,
Never, Never,
} }
#[derive(Clone, Debug)] #[derive(Debug)]
pub struct MacroMatcher { pub struct MacroMatcher {
pub name: String, pub name: String,
pub braces: (String, String), pub braces: (char, char),
} }
impl Hash for MacroMatcher {
fn hash<H: Hasher>(&self, state: &mut H) {
self.name.hash(state);
}
}
impl PartialEq for MacroMatcher {
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl Eq for MacroMatcher {}
impl<'de> Deserialize<'de> for MacroMatcher { impl<'de> Deserialize<'de> for MacroMatcher {
fn deserialize<D>(deser: D) -> Result<Self, D::Error> fn deserialize<D>(deser: D) -> Result<Self, D::Error>
where where
@ -83,7 +69,7 @@ impl<'de> Deserialize<'de> for MacroMatcher {
V: de::MapAccess<'de>, V: de::MapAccess<'de>,
{ {
let mut name = None; let mut name = None;
let mut brace: Option<String> = None; let mut brace: Option<char> = None;
while let Some(key) = map.next_key()? { while let Some(key) = map.next_key()? {
match key { match key {
Field::Name => { Field::Name => {
@ -104,7 +90,7 @@ impl<'de> Deserialize<'de> for MacroMatcher {
let brace = brace.ok_or_else(|| de::Error::missing_field("brace"))?; let brace = brace.ok_or_else(|| de::Error::missing_field("brace"))?;
Ok(MacroMatcher { Ok(MacroMatcher {
name, name,
braces: [("(", ")"), ("{", "}"), ("[", "]")] braces: [('(', ')'), ('{', '}'), ('[', ']')]
.into_iter() .into_iter()
.find(|b| b.0 == brace) .find(|b| b.0 == brace)
.map(|(o, c)| (o.to_owned(), c.to_owned())) .map(|(o, c)| (o.to_owned(), c.to_owned()))

View file

@ -320,8 +320,8 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
extract_msrv_attr!({context_import}); extract_msrv_attr!({context_import});
}} }}
// TODO: Add MSRV level to `clippy_utils/src/msrvs.rs` if needed. // TODO: Add MSRV level to `clippy_config/src/msrvs.rs` if needed.
// TODO: Update msrv config comment in `clippy_lints/src/utils/conf.rs` // TODO: Update msrv config comment in `clippy_config/src/conf.rs`
"# "#
) )
} else { } else {

View file

@ -1,6 +1,6 @@
[package] [package]
name = "clippy_lints" name = "clippy_lints"
version = "0.1.75" version = "0.1.76"
description = "A bunch of helpful lints to avoid common pitfalls in Rust" description = "A bunch of helpful lints to avoid common pitfalls in Rust"
repository = "https://github.com/rust-lang/rust-clippy" repository = "https://github.com/rust-lang/rust-clippy"
readme = "README.md" readme = "README.md"
@ -14,7 +14,6 @@ cargo_metadata = "0.15.3"
clippy_config = { path = "../clippy_config" } clippy_config = { path = "../clippy_config" }
clippy_utils = { path = "../clippy_utils" } clippy_utils = { path = "../clippy_utils" }
declare_clippy_lint = { path = "../declare_clippy_lint" } declare_clippy_lint = { path = "../declare_clippy_lint" }
if_chain = "1.0"
itertools = "0.10.1" itertools = "0.10.1"
quine-mc_cluskey = "0.2" quine-mc_cluskey = "0.2"
regex-syntax = "0.7" regex-syntax = "0.7"

View file

@ -52,14 +52,13 @@ declare_lint_pass!(AllowAttribute => [ALLOW_ATTRIBUTES]);
impl LateLintPass<'_> for AllowAttribute { impl LateLintPass<'_> for AllowAttribute {
// Separate each crate's features. // Separate each crate's features.
fn check_attribute<'cx>(&mut self, cx: &LateContext<'cx>, attr: &'cx Attribute) { fn check_attribute<'cx>(&mut self, cx: &LateContext<'cx>, attr: &'cx Attribute) {
if_chain! { if !in_external_macro(cx.sess(), attr.span)
if !in_external_macro(cx.sess(), attr.span); && cx.tcx.features().lint_reasons
if cx.tcx.features().lint_reasons; && let AttrStyle::Outer = attr.style
if let AttrStyle::Outer = attr.style; && let Some(ident) = attr.ident()
if let Some(ident) = attr.ident(); && ident.name == rustc_span::symbol::sym::allow
if ident.name == rustc_span::symbol::sym::allow; && !is_from_proc_macro(cx, &attr)
if !is_from_proc_macro(cx, &attr); {
then {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
ALLOW_ATTRIBUTES, ALLOW_ATTRIBUTES,
@ -71,5 +70,4 @@ impl LateLintPass<'_> for AllowAttribute {
); );
} }
} }
}
} }

View file

@ -14,7 +14,9 @@ declare_clippy_lint! {
/// This lint warns when you use `Arc` with a type that does not implement `Send` or `Sync`. /// This lint warns when you use `Arc` with a type that does not implement `Send` or `Sync`.
/// ///
/// ### Why is this bad? /// ### Why is this bad?
/// `Arc<T>` is only `Send`/`Sync` when `T` is [both `Send` and `Sync`](https://doc.rust-lang.org/std/sync/struct.Arc.html#impl-Send-for-Arc%3CT%3E), /// `Arc<T>` is an Atomic `RC<T>` and guarantees that updates to the reference counter are
/// Atomic. This is useful in multiprocessing scenarios. To send an `Arc<T>` across processes
/// and make use of the atomic ref counter, `T` must be [both `Send` and `Sync`](https://doc.rust-lang.org/std/sync/struct.Arc.html#impl-Send-for-Arc%3CT%3E),
/// either `T` should be made `Send + Sync` or an `Rc` should be used instead of an `Arc` /// either `T` should be made `Send + Sync` or an `Rc` should be used instead of an `Arc`
/// ///
/// ### Example /// ### Example
@ -34,7 +36,7 @@ declare_clippy_lint! {
#[clippy::version = "1.72.0"] #[clippy::version = "1.72.0"]
pub ARC_WITH_NON_SEND_SYNC, pub ARC_WITH_NON_SEND_SYNC,
suspicious, suspicious,
"using `Arc` with a type that does not implement `Send` or `Sync`" "using `Arc` with a type that does not implement `Send` and `Sync`"
} }
declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]); declare_lint_pass!(ArcWithNonSendSync => [ARC_WITH_NON_SEND_SYNC]);
@ -61,19 +63,25 @@ impl<'tcx> LateLintPass<'tcx> for ArcWithNonSendSync {
cx, cx,
ARC_WITH_NON_SEND_SYNC, ARC_WITH_NON_SEND_SYNC,
expr.span, expr.span,
"usage of an `Arc` that is not `Send` or `Sync`", "usage of an `Arc` that is not `Send` and `Sync`",
|diag| { |diag| {
with_forced_trimmed_paths!({ with_forced_trimmed_paths!({
diag.note(format!("`Arc<{arg_ty}>` is not `Send` and `Sync` as:"));
if !is_send { if !is_send {
diag.note(format!("the trait `Send` is not implemented for `{arg_ty}`")); diag.note(format!("- the trait `Send` is not implemented for `{arg_ty}`"));
} }
if !is_sync { if !is_sync {
diag.note(format!("the trait `Sync` is not implemented for `{arg_ty}`")); diag.note(format!("- the trait `Sync` is not implemented for `{arg_ty}`"));
} }
diag.note(format!("required for `{ty}` to implement `Send` and `Sync`")); diag.help("consider using an `Rc` instead. `Arc` does not provide benefits for non `Send` and `Sync` types");
diag.help("consider using an `Rc` instead or wrapping the inner type with a `Mutex`"); diag.note("if you intend to use `Arc` with `Send` and `Sync` traits");
diag.note(format!(
"wrap the inner type with a `Mutex` or implement `Send` and `Sync` for `{arg_ty}`"
));
}); });
}, },
); );

View file

@ -5,7 +5,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sug
use clippy_utils::is_from_proc_macro; use clippy_utils::is_from_proc_macro;
use clippy_utils::macros::{is_panic, macro_backtrace}; use clippy_utils::macros::{is_panic, macro_backtrace};
use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments}; use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
use if_chain::if_chain;
use rustc_ast::token::{Token, TokenKind}; use rustc_ast::token::{Token, TokenKind};
use rustc_ast::tokenstream::TokenTree; use rustc_ast::tokenstream::TokenTree;
use rustc_ast::{ use rustc_ast::{
@ -20,7 +19,7 @@ use rustc_middle::lint::in_external_macro;
use rustc_middle::ty; use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
use rustc_span::{sym, DUMMY_SP, Span}; use rustc_span::{sym, Span, DUMMY_SP};
use semver::Version; use semver::Version;
static UNIX_SYSTEMS: &[&str] = &[ static UNIX_SYSTEMS: &[&str] = &[
@ -371,7 +370,7 @@ declare_clippy_lint! {
/// let _ = 1 / random(); /// let _ = 1 / random();
/// } /// }
/// ``` /// ```
#[clippy::version = "1.73.0"] #[clippy::version = "1.74.0"]
pub SHOULD_PANIC_WITHOUT_EXPECT, pub SHOULD_PANIC_WITHOUT_EXPECT,
pedantic, pedantic,
"ensures that all `should_panic` attributes specify its expected panic message" "ensures that all `should_panic` attributes specify its expected panic message"
@ -470,17 +469,15 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
return; return;
} }
for item in items { for item in items {
if_chain! { if let NestedMetaItem::MetaItem(mi) = &item
if let NestedMetaItem::MetaItem(mi) = &item; && let MetaItemKind::NameValue(lit) = &mi.kind
if let MetaItemKind::NameValue(lit) = &mi.kind; && mi.has_name(sym::since)
if mi.has_name(sym::since); {
then {
check_semver(cx, item.span(), lit); check_semver(cx, item.span(), lit);
} }
} }
} }
} }
}
if attr.has_name(sym::should_panic) { if attr.has_name(sym::should_panic) {
check_should_panic_reason(cx, attr); check_should_panic_reason(cx, attr);
} }
@ -579,16 +576,14 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
/// Returns the lint name if it is clippy lint. /// Returns the lint name if it is clippy lint.
fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<Symbol> { fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<Symbol> {
if_chain! { if let Some(meta_item) = lint.meta_item()
if let Some(meta_item) = lint.meta_item(); && meta_item.path.segments.len() > 1
if meta_item.path.segments.len() > 1; && let tool_name = meta_item.path.segments[0].ident
if let tool_name = meta_item.path.segments[0].ident; && tool_name.name == sym::clippy
if tool_name.name == sym::clippy; {
then {
let lint_name = meta_item.path.segments.last().unwrap().ident.name; let lint_name = meta_item.path.segments.last().unwrap().ident.name;
return Some(lint_name); return Some(lint_name);
} }
}
None None
} }
@ -856,18 +851,17 @@ fn check_empty_line_after_outer_attr(cx: &EarlyContext<'_>, item: &rustc_ast::It
} }
fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) { fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msrv) {
if_chain! { if msrv.meets(msrvs::TOOL_ATTRIBUTES)
if msrv.meets(msrvs::TOOL_ATTRIBUTES);
// check cfg_attr // check cfg_attr
if attr.has_name(sym::cfg_attr); && attr.has_name(sym::cfg_attr)
if let Some(items) = attr.meta_item_list(); && let Some(items) = attr.meta_item_list()
if items.len() == 2; && items.len() == 2
// check for `rustfmt` // check for `rustfmt`
if let Some(feature_item) = items[0].meta_item(); && let Some(feature_item) = items[0].meta_item()
if feature_item.has_name(sym::rustfmt); && feature_item.has_name(sym::rustfmt)
// check for `rustfmt_skip` and `rustfmt::skip` // check for `rustfmt_skip` and `rustfmt::skip`
if let Some(skip_item) = &items[1].meta_item(); && let Some(skip_item) = &items[1].meta_item()
if skip_item.has_name(sym!(rustfmt_skip)) && (skip_item.has_name(sym!(rustfmt_skip))
|| skip_item || skip_item
.path .path
.segments .segments
@ -875,11 +869,11 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msr
.expect("empty path in attribute") .expect("empty path in attribute")
.ident .ident
.name .name
== sym::skip; == sym::skip)
// Only lint outer attributes, because custom inner attributes are unstable // Only lint outer attributes, because custom inner attributes are unstable
// Tracking issue: https://github.com/rust-lang/rust/issues/54726 // Tracking issue: https://github.com/rust-lang/rust/issues/54726
if attr.style == AttrStyle::Outer; && attr.style == AttrStyle::Outer
then { {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
DEPRECATED_CFG_ATTR, DEPRECATED_CFG_ATTR,
@ -890,7 +884,6 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msr
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }
}
} }
fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) { fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
@ -989,13 +982,11 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
mismatched.extend(find_mismatched_target_os(list)); mismatched.extend(find_mismatched_target_os(list));
}, },
MetaItemKind::Word => { MetaItemKind::Word => {
if_chain! { if let Some(ident) = meta.ident()
if let Some(ident) = meta.ident(); && let Some(os) = find_os(ident.name.as_str())
if let Some(os) = find_os(ident.name.as_str()); {
then {
mismatched.push((os, ident.span)); mismatched.push((os, ident.span));
} }
}
}, },
MetaItemKind::NameValue(..) => {}, MetaItemKind::NameValue(..) => {},
} }
@ -1005,12 +996,11 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
mismatched mismatched
} }
if_chain! { if attr.has_name(sym::cfg)
if attr.has_name(sym::cfg); && let Some(list) = attr.meta_item_list()
if let Some(list) = attr.meta_item_list(); && let mismatched = find_mismatched_target_os(&list)
let mismatched = find_mismatched_target_os(&list); && !mismatched.is_empty()
if !mismatched.is_empty(); {
then {
let mess = "operating system used in target family position"; let mess = "operating system used in target family position";
span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| { span_lint_and_then(cx, MISMATCHED_TARGET_OS, attr.span, mess, |diag| {
@ -1029,7 +1019,6 @@ fn check_mismatched_target_os(cx: &EarlyContext<'_>, attr: &Attribute) {
} }
}); });
} }
}
} }
fn is_lint_level(symbol: Symbol) -> bool { fn is_lint_level(symbol: Symbol) -> bool {

View file

@ -4,7 +4,6 @@ use clippy_utils::ty::implements_trait;
use clippy_utils::visitors::{for_each_expr, Descend}; use clippy_utils::visitors::{for_each_expr, Descend};
use clippy_utils::{get_parent_expr, higher}; use clippy_utils::{get_parent_expr, higher};
use core::ops::ControlFlow; use core::ops::ControlFlow;
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BlockCheckMode, Expr, ExprKind}; use rustc_hir::{BlockCheckMode, Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -114,16 +113,14 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
let _: Option<!> = for_each_expr(cond, |e| { let _: Option<!> = for_each_expr(cond, |e| {
if let ExprKind::Closure(closure) = e.kind { if let ExprKind::Closure(closure) = e.kind {
// do not lint if the closure is called using an iterator (see #1141) // do not lint if the closure is called using an iterator (see #1141)
if_chain! { if let Some(parent) = get_parent_expr(cx, e)
if let Some(parent) = get_parent_expr(cx, e); && let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind
if let ExprKind::MethodCall(_, self_arg, _, _) = &parent.kind; && let caller = cx.typeck_results().expr_ty(self_arg)
let caller = cx.typeck_results().expr_ty(self_arg); && let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator)
if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator); && implements_trait(cx, caller, iter_id, &[])
if implements_trait(cx, caller, iter_id, &[]); {
then {
return ControlFlow::Continue(Descend::No); return ControlFlow::Continue(Descend::No);
} }
}
let body = cx.tcx.hir().body(closure.body); let body = cx.tcx.hir().body(closure.body);
let ex = &body.value; let ex = &body.value;

View file

@ -2,7 +2,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
use clippy_utils::eq_expr_value; use clippy_utils::eq_expr_value;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use if_chain::if_chain;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, FnKind, Visitor}; use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
@ -151,19 +150,17 @@ impl<'a, 'tcx, 'v> Hir2Qmm<'a, 'tcx, 'v> {
return Ok(Bool::Term(n as u8)); return Ok(Bool::Term(n as u8));
} }
if_chain! { if let ExprKind::Binary(e_binop, e_lhs, e_rhs) = &e.kind
if let ExprKind::Binary(e_binop, e_lhs, e_rhs) = &e.kind; && implements_ord(self.cx, e_lhs)
if implements_ord(self.cx, e_lhs); && let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind
if let ExprKind::Binary(expr_binop, expr_lhs, expr_rhs) = &expr.kind; && negate(e_binop.node) == Some(expr_binop.node)
if negate(e_binop.node) == Some(expr_binop.node); && eq_expr_value(self.cx, e_lhs, expr_lhs)
if eq_expr_value(self.cx, e_lhs, expr_lhs); && eq_expr_value(self.cx, e_rhs, expr_rhs)
if eq_expr_value(self.cx, e_rhs, expr_rhs); {
then {
#[expect(clippy::cast_possible_truncation)] #[expect(clippy::cast_possible_truncation)]
return Ok(Bool::Not(Box::new(Bool::Term(n as u8)))); return Ok(Bool::Not(Box::new(Bool::Term(n as u8))));
} }
} }
}
let n = self.terminals.len(); let n = self.terminals.len();
self.terminals.push(e); self.terminals.push(e);
if n < 32 { if n < 32 {

View file

@ -49,30 +49,29 @@ declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]);
impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) {
if_chain! { if !e.span.from_expansion()
if !e.span.from_expansion(); && let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind
if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind; && !addrof_target.span.from_expansion()
if !addrof_target.span.from_expansion(); && let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind
if let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind; && !deref_target.span.from_expansion()
if !deref_target.span.from_expansion(); && !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..))
if !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..) ); && let ref_ty = cx.typeck_results().expr_ty(deref_target)
let ref_ty = cx.typeck_results().expr_ty(deref_target); && let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind()
if let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind(); && !is_from_proc_macro(cx, e)
if !is_from_proc_macro(cx, e); {
then{ if let Some(parent_expr) = get_parent_expr(cx, e) {
if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..))
if let Some(parent_expr) = get_parent_expr(cx, e){ && !is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id)
if matches!(parent_expr.kind, ExprKind::Unary(UnOp::Deref, ..)) && {
!is_lint_allowed(cx, DEREF_ADDROF, parent_expr.hir_id) {
return; return;
} }
// modification to `&mut &*x` is different from `&mut x` // modification to `&mut &*x` is different from `&mut x`
if matches!(deref_target.kind, ExprKind::Path(..) if matches!(
| ExprKind::Field(..) deref_target.kind,
| ExprKind::Index(..) ExprKind::Path(..) | ExprKind::Field(..) | ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, ..)
| ExprKind::Unary(UnOp::Deref, ..)) ) && matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _))
&& matches!(parent_expr.kind, ExprKind::AddrOf(_, Mutability::Mut, _)) { {
return; return;
} }
} }
@ -87,12 +86,12 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
e.span, e.span,
"if you would like to reborrow, try removing `&*`", "if you would like to reborrow, try removing `&*`",
snippet_opt(cx, deref_target.span).unwrap(), snippet_opt(cx, deref_target.span).unwrap(),
Applicability::MachineApplicable Applicability::MachineApplicable,
); );
// has deref trait -> give 2 help // has deref trait -> give 2 help
// doesn't have deref trait -> give 1 help // doesn't have deref trait -> give 1 help
if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait(){ if let Some(deref_trait_id) = cx.tcx.lang_items().deref_trait() {
if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) { if !implements_trait(cx, *inner_ty, deref_trait_id, &[]) {
return; return;
} }
@ -101,17 +100,11 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
diag.span_suggestion( diag.span_suggestion(
e.span, e.span,
"if you would like to deref, try using `&**`", "if you would like to deref, try using `&**`",
format!( format!("&**{}", &snippet_opt(cx, deref_target.span).unwrap()),
"&**{}", Applicability::MaybeIncorrect,
&snippet_opt(cx, deref_target.span).unwrap(),
),
Applicability::MaybeIncorrect
); );
},
}
); );
}
} }
} }
} }

View file

@ -2,7 +2,6 @@
use cargo_metadata::{DependencyKind, Metadata, Node, Package, PackageId}; use cargo_metadata::{DependencyKind, Metadata, Node, Package, PackageId};
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use if_chain::if_chain;
use itertools::Itertools; use itertools::Itertools;
use rustc_hir::def_id::LOCAL_CRATE; use rustc_hir::def_id::LOCAL_CRATE;
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -15,12 +14,15 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) {
let mut packages = metadata.packages.clone(); let mut packages = metadata.packages.clone();
packages.sort_by(|a, b| a.name.cmp(&b.name)); packages.sort_by(|a, b| a.name.cmp(&b.name));
if_chain! { if let Some(resolve) = &metadata.resolve
if let Some(resolve) = &metadata.resolve; && let Some(local_id) = packages.iter().find_map(|p| {
if let Some(local_id) = packages if p.name == local_name.as_str() {
.iter() Some(&p.id)
.find_map(|p| if p.name == local_name.as_str() { Some(&p.id) } else { None }); } else {
then { None
}
})
{
for (name, group) in &packages.iter().group_by(|p| p.name.clone()) { for (name, group) in &packages.iter().group_by(|p| p.name.clone()) {
let group: Vec<&Package> = group.collect(); let group: Vec<&Package> = group.collect();
@ -42,7 +44,6 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) {
} }
} }
} }
}
} }
fn is_normal_dep(nodes: &[Node], local_id: &PackageId, dep_id: &PackageId) -> bool { fn is_normal_dep(nodes: &[Node], local_id: &PackageId, dep_id: &PackageId) -> bool {

View file

@ -1,6 +1,5 @@
use cargo_metadata::Metadata; use cargo_metadata::Metadata;
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use if_chain::if_chain;
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::DUMMY_SP; use rustc_span::DUMMY_SP;
@ -9,12 +8,11 @@ use super::WILDCARD_DEPENDENCIES;
pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) { pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) {
for dep in &metadata.packages[0].dependencies { for dep in &metadata.packages[0].dependencies {
// VersionReq::any() does not work // VersionReq::any() does not work
if_chain! { if let Ok(wildcard_ver) = semver::VersionReq::parse("*")
if let Ok(wildcard_ver) = semver::VersionReq::parse("*"); && let Some(ref source) = dep.source
if let Some(ref source) = dep.source; && !source.starts_with("git")
if !source.starts_with("git"); && dep.req == wildcard_ver
if dep.req == wildcard_ver; {
then {
span_lint( span_lint(
cx, cx,
WILDCARD_DEPENDENCIES, WILDCARD_DEPENDENCIES,
@ -23,5 +21,4 @@ pub(super) fn check(cx: &LateContext<'_>, metadata: &Metadata) {
); );
} }
} }
}
} }

View file

@ -1,5 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_hir::Expr; use rustc_hir::Expr;
use rustc_lint::{LateContext, LintContext}; use rustc_lint::LateContext;
use rustc_middle::ty::Ty; use rustc_middle::ty::Ty;
use super::{utils, CAST_POSSIBLE_WRAP}; use super::{utils, CAST_POSSIBLE_WRAP};
@ -78,13 +79,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca
), ),
}; };
cx.struct_span_lint(CAST_POSSIBLE_WRAP, expr.span, message, |diag| { span_lint_and_then(cx, CAST_POSSIBLE_WRAP, expr.span, &message, |diag| {
if let EmitState::LintOnPtrSize(16) = should_lint { if let EmitState::LintOnPtrSize(16) = should_lint {
diag diag
.note("`usize` and `isize` may be as small as 16 bits on some platforms") .note("`usize` and `isize` may be as small as 16 bits on some platforms")
.note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types") .note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types");
} else { };
diag
}
}); });
} }

View file

@ -1,7 +1,6 @@
use clippy_utils::consts::{constant, Constant}; use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::{method_chain_args, sext}; use clippy_utils::{method_chain_args, sext};
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_middle::ty::{self, Ty}; use rustc_middle::ty::{self, Ty};
@ -28,28 +27,24 @@ fn should_lint(cx: &LateContext<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast
// Don't lint for positive constants. // Don't lint for positive constants.
let const_val = constant(cx, cx.typeck_results(), cast_op); let const_val = constant(cx, cx.typeck_results(), cast_op);
if_chain! { if let Some(Constant::Int(n)) = const_val
if let Some(Constant::Int(n)) = const_val; && let ty::Int(ity) = *cast_from.kind()
if let ty::Int(ity) = *cast_from.kind(); && sext(cx.tcx, n, ity) >= 0
if sext(cx.tcx, n, ity) >= 0; {
then {
return false; return false;
} }
}
// Don't lint for the result of methods that always return non-negative values. // Don't lint for the result of methods that always return non-negative values.
if let ExprKind::MethodCall(path, ..) = cast_op.kind { if let ExprKind::MethodCall(path, ..) = cast_op.kind {
let mut method_name = path.ident.name.as_str(); let mut method_name = path.ident.name.as_str();
let allowed_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"]; let allowed_methods = ["abs", "checked_abs", "rem_euclid", "checked_rem_euclid"];
if_chain! { if method_name == "unwrap"
if method_name == "unwrap"; && let Some(arglist) = method_chain_args(cast_op, &["unwrap"])
if let Some(arglist) = method_chain_args(cast_op, &["unwrap"]); && let ExprKind::MethodCall(inner_path, ..) = &arglist[0].0.kind
if let ExprKind::MethodCall(inner_path, ..) = &arglist[0].0.kind; {
then {
method_name = inner_path.ident.name.as_str(); method_name = inner_path.ident.name.as_str();
} }
}
if allowed_methods.iter().any(|&name| method_name == name) { if allowed_methods.iter().any(|&name| method_name == name) {
return false; return false;

View file

@ -1,7 +1,6 @@
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source; use clippy_utils::source;
use if_chain::if_chain;
use rustc_ast::Mutability; use rustc_ast::Mutability;
use rustc_hir::{Expr, ExprKind, Node}; use rustc_hir::{Expr, ExprKind, Node};
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -69,10 +68,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: &Msrv
fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let map = cx.tcx.hir(); let map = cx.tcx.hir();
if_chain! { if let Some(parent_id) = map.opt_parent_id(expr.hir_id)
if let Some(parent_id) = map.opt_parent_id(expr.hir_id); && let Some(parent) = map.find(parent_id)
if let Some(parent) = map.find(parent_id); {
then {
let expr = match parent { let expr = match parent {
Node::Block(block) => { Node::Block(block) => {
if let Some(parent_expr) = block.expr { if let Some(parent_expr) = block.expr {
@ -89,7 +87,6 @@ fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
} else { } else {
false false
} }
}
} }
/// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if /// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if

View file

@ -1,7 +1,6 @@
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_context; use clippy_utils::source::snippet_with_context;
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
@ -25,20 +24,19 @@ fn raw_parts_kind(cx: &LateContext<'_>, did: DefId) -> Option<RawPartsKind> {
} }
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) { pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>, msrv: &Msrv) {
if_chain! { if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS)
if msrv.meets(msrvs::PTR_SLICE_RAW_PARTS); && let ty::RawPtr(ptrty) = cast_to.kind()
if let ty::RawPtr(ptrty) = cast_to.kind(); && let ty::Slice(_) = ptrty.ty.kind()
if let ty::Slice(_) = ptrty.ty.kind(); && let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind
if let ExprKind::Call(fun, [ptr_arg, len_arg]) = cast_expr.peel_blocks().kind; && let ExprKind::Path(ref qpath) = fun.kind
if let ExprKind::Path(ref qpath) = fun.kind; && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id()
if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id(); && let Some(rpk) = raw_parts_kind(cx, fun_def_id)
if let Some(rpk) = raw_parts_kind(cx, fun_def_id); && let ctxt = expr.span.ctxt()
let ctxt = expr.span.ctxt(); && cast_expr.span.ctxt() == ctxt
if cast_expr.span.ctxt() == ctxt; {
then {
let func = match rpk { let func = match rpk {
RawPartsKind::Immutable => "from_raw_parts", RawPartsKind::Immutable => "from_raw_parts",
RawPartsKind::Mutable => "from_raw_parts_mut" RawPartsKind::Mutable => "from_raw_parts_mut",
}; };
let span = expr.span; let span = expr.span;
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
@ -51,8 +49,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
&format!("casting the result of `{func}` to {cast_to}"), &format!("casting the result of `{func}` to {cast_to}"),
"replace with", "replace with",
format!("core::ptr::slice_{func}({ptr}, {len})"), format!("core::ptr::slice_{func}({ptr}, {len})"),
applicability applicability,
); );
} }
}
} }

View file

@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use if_chain::if_chain;
use rustc_ast::LitKind; use rustc_ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
@ -10,12 +9,11 @@ use rustc_middle::ty::{self, UintTy};
use super::CHAR_LIT_AS_U8; use super::CHAR_LIT_AS_U8;
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! { if let ExprKind::Cast(e, _) = &expr.kind
if let ExprKind::Cast(e, _) = &expr.kind; && let ExprKind::Lit(l) = &e.kind
if let ExprKind::Lit(l) = &e.kind; && let LitKind::Char(c) = l.node
if let LitKind::Char(c) = l.node; && ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(expr).kind()
if ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(expr).kind(); {
then {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let snippet = snippet_with_applicability(cx, e.span, "'x'", &mut applicability); let snippet = snippet_with_applicability(cx, e.span, "'x'", &mut applicability);
@ -35,7 +33,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
applicability, applicability,
); );
} }
}); },
} );
} }
} }

View file

@ -1,7 +1,6 @@
use clippy_config::msrvs::{self, Msrv}; use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, Mutability}; use rustc_hir::{Expr, Mutability};
use rustc_lint::LateContext; use rustc_lint::LateContext;
@ -17,14 +16,21 @@ pub(super) fn check<'tcx>(
cast_to: Ty<'tcx>, cast_to: Ty<'tcx>,
msrv: &Msrv, msrv: &Msrv,
) { ) {
if_chain! { if msrv.meets(msrvs::POINTER_CAST_CONSTNESS)
if msrv.meets(msrvs::POINTER_CAST_CONSTNESS); && let ty::RawPtr(TypeAndMut {
if let ty::RawPtr(TypeAndMut { mutbl: from_mutbl, ty: from_ty }) = cast_from.kind(); mutbl: from_mutbl,
if let ty::RawPtr(TypeAndMut { mutbl: to_mutbl, ty: to_ty }) = cast_to.kind(); ty: from_ty,
if matches!((from_mutbl, to_mutbl), }) = cast_from.kind()
(Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not)); && let ty::RawPtr(TypeAndMut {
if from_ty == to_ty; mutbl: to_mutbl,
then { ty: to_ty,
}) = cast_to.kind()
&& matches!(
(from_mutbl, to_mutbl),
(Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not)
)
&& from_ty == to_ty
{
let sugg = Sugg::hir(cx, cast_expr, "_"); let sugg = Sugg::hir(cx, cast_expr, "_");
let constness = match *to_mutbl { let constness = match *to_mutbl {
Mutability::Not => "const", Mutability::Not => "const",
@ -41,5 +47,4 @@ pub(super) fn check<'tcx>(
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }
}
} }

View file

@ -3,7 +3,6 @@ use clippy_utils::numeric_literal::NumericLiteral;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::visitors::{for_each_expr, Visitable}; use clippy_utils::visitors::{for_each_expr, Visitable};
use clippy_utils::{get_parent_expr, get_parent_node, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local}; use clippy_utils::{get_parent_expr, get_parent_node, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
use if_chain::if_chain;
use rustc_ast::{LitFloatType, LitIntType, LitKind}; use rustc_ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
@ -25,16 +24,15 @@ pub(super) fn check<'tcx>(
) -> bool { ) -> bool {
let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default(); let cast_str = snippet_opt(cx, cast_expr.span).unwrap_or_default();
if_chain! { if let ty::RawPtr(..) = cast_from.kind()
if let ty::RawPtr(..) = cast_from.kind();
// check both mutability and type are the same // check both mutability and type are the same
if cast_from.kind() == cast_to.kind(); && cast_from.kind() == cast_to.kind()
if let ExprKind::Cast(_, cast_to_hir) = expr.kind; && let ExprKind::Cast(_, cast_to_hir) = expr.kind
// Ignore casts to e.g. type aliases and infer types // Ignore casts to e.g. type aliases and infer types
// - p as pointer_alias // - p as pointer_alias
// - p as _ // - p as _
if let TyKind::Ptr(to_pointee) = cast_to_hir.kind; && let TyKind::Ptr(to_pointee) = cast_to_hir.kind
then { {
match to_pointee.ty.kind { match to_pointee.ty.kind {
// Ignore casts to pointers that are aliases or cfg dependant, e.g. // Ignore casts to pointers that are aliases or cfg dependant, e.g.
// - p as *const std::ffi::c_char (alias) // - p as *const std::ffi::c_char (alias)
@ -53,13 +51,14 @@ pub(super) fn check<'tcx>(
cx, cx,
UNNECESSARY_CAST, UNNECESSARY_CAST,
expr.span, expr.span,
&format!("casting raw pointers to the same type and constness is unnecessary (`{cast_from}` -> `{cast_to}`)"), &format!(
"casting raw pointers to the same type and constness is unnecessary (`{cast_from}` -> `{cast_to}`)"
),
"try", "try",
cast_str.clone(), cast_str.clone(),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
} }
}
// skip cast of local that is a type alias // skip cast of local that is a type alias
if let ExprKind::Cast(inner, ..) = expr.kind if let ExprKind::Cast(inner, ..) = expr.kind
@ -86,15 +85,13 @@ pub(super) fn check<'tcx>(
} }
// skip cast to non-primitive type // skip cast to non-primitive type
if_chain! { if let ExprKind::Cast(_, cast_to) = expr.kind
if let ExprKind::Cast(_, cast_to) = expr.kind; && let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind
if let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind; && let Res::PrimTy(_) = path.res
if let Res::PrimTy(_) = path.res; {
then {} } else {
else {
return false; return false;
} }
}
// skip cast of fn call that returns type alias // skip cast of fn call that returns type alias
if let ExprKind::Cast(inner, ..) = expr.kind if let ExprKind::Cast(inner, ..) = expr.kind
@ -106,18 +103,19 @@ pub(super) fn check<'tcx>(
if let Some(lit) = get_numeric_literal(cast_expr) { if let Some(lit) = get_numeric_literal(cast_expr) {
let literal_str = &cast_str; let literal_str = &cast_str;
if_chain! { if let LitKind::Int(n, _) = lit.node
if let LitKind::Int(n, _) = lit.node; && let Some(src) = snippet_opt(cx, cast_expr.span)
if let Some(src) = snippet_opt(cx, cast_expr.span); && cast_to.is_floating_point()
if cast_to.is_floating_point(); && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node)
if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node); && let from_nbits = 128 - n.leading_zeros()
let from_nbits = 128 - n.leading_zeros(); && let to_nbits = fp_ty_mantissa_nbits(cast_to)
let to_nbits = fp_ty_mantissa_nbits(cast_to); && from_nbits != 0
if from_nbits != 0 && to_nbits != 0 && from_nbits <= to_nbits && num_lit.is_decimal(); && to_nbits != 0
then { && from_nbits <= to_nbits
&& num_lit.is_decimal()
{
lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
return true return true;
}
} }
match lit.node { match lit.node {

View file

@ -4,7 +4,6 @@ use clippy_config::msrvs::{self, Msrv};
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{in_constant, is_integer_literal, SpanlessEq}; use clippy_utils::{in_constant, is_integer_literal, SpanlessEq};
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind}; use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, QPath, TyKind};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
@ -55,12 +54,10 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
return; return;
} }
let result = if_chain! { let result = if !in_constant(cx, item.hir_id)
if !in_constant(cx, item.hir_id); && !in_external_macro(cx.sess(), item.span)
if !in_external_macro(cx.sess(), item.span); && let ExprKind::Binary(op, left, right) = &item.kind
if let ExprKind::Binary(op, left, right) = &item.kind; {
then {
match op.node { match op.node {
BinOpKind::Ge | BinOpKind::Le => single_check(item), BinOpKind::Ge | BinOpKind::Le => single_check(item),
BinOpKind::And => double_check(cx, left, right), BinOpKind::And => double_check(cx, left, right),
@ -68,7 +65,6 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
} }
} else { } else {
None None
}
}; };
if let Some(cv) = result { if let Some(cv) = result {
@ -193,17 +189,14 @@ impl ConversionType {
/// Check for `expr <= (to_type::MAX as from_type)` /// Check for `expr <= (to_type::MAX as from_type)`
fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> { fn check_upper_bound<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<Conversion<'tcx>> {
if_chain! { if let ExprKind::Binary(ref op, left, right) = &expr.kind
if let ExprKind::Binary(ref op, left, right) = &expr.kind; && let Some((candidate, check)) = normalize_le_ge(op, left, right)
if let Some((candidate, check)) = normalize_le_ge(op, left, right); && let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX")
if let Some((from, to)) = get_types_from_cast(check, INTS, "max_value", "MAX"); {
then {
Conversion::try_new(candidate, from, to) Conversion::try_new(candidate, from, to)
} else { } else {
None None
} }
}
} }
/// Check for `expr >= 0|(to_type::MIN as from_type)` /// Check for `expr >= 0|(to_type::MIN as from_type)`
@ -243,34 +236,28 @@ fn get_types_from_cast<'a>(
) -> Option<(&'a str, &'a str)> { ) -> Option<(&'a str, &'a str)> {
// `to_type::max_value() as from_type` // `to_type::max_value() as from_type`
// or `to_type::MAX as from_type` // or `to_type::MAX as from_type`
let call_from_cast: Option<(&Expr<'_>, &str)> = if_chain! { let call_from_cast: Option<(&Expr<'_>, &str)> = if let ExprKind::Cast(limit, from_type) = &expr.kind
// to_type::max_value(), from_type // to_type::max_value(), from_type
if let ExprKind::Cast(limit, from_type) = &expr.kind; && let TyKind::Path(ref from_type_path) = &from_type.kind
if let TyKind::Path(ref from_type_path) = &from_type.kind; && let Some(from_sym) = int_ty_to_sym(from_type_path)
if let Some(from_sym) = int_ty_to_sym(from_type_path); {
then {
Some((limit, from_sym)) Some((limit, from_sym))
} else { } else {
None None
}
}; };
// `from_type::from(to_type::max_value())` // `from_type::from(to_type::max_value())`
let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| { let limit_from: Option<(&Expr<'_>, &str)> = call_from_cast.or_else(|| {
if_chain! { if let ExprKind::Call(from_func, [limit]) = &expr.kind
// `from_type::from, to_type::max_value()` // `from_type::from, to_type::max_value()`
if let ExprKind::Call(from_func, [limit]) = &expr.kind;
// `from_type::from` // `from_type::from`
if let ExprKind::Path(ref path) = &from_func.kind; && let ExprKind::Path(ref path) = &from_func.kind
if let Some(from_sym) = get_implementing_type(path, INTS, "from"); && let Some(from_sym) = get_implementing_type(path, INTS, "from")
{
then {
Some((limit, from_sym)) Some((limit, from_sym))
} else { } else {
None None
} }
}
}); });
if let Some((limit, from_type)) = limit_from { if let Some((limit, from_type)) = limit_from {
@ -298,32 +285,28 @@ fn get_types_from_cast<'a>(
/// Gets the type which implements the called function /// Gets the type which implements the called function
fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: &str) -> Option<&'a str> { fn get_implementing_type<'a>(path: &QPath<'_>, candidates: &'a [&str], function: &str) -> Option<&'a str> {
if_chain! { if let QPath::TypeRelative(ty, path) = &path
if let QPath::TypeRelative(ty, path) = &path; && path.ident.name.as_str() == function
if path.ident.name.as_str() == function; && let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind
if let TyKind::Path(QPath::Resolved(None, tp)) = &ty.kind; && let [int] = tp.segments
if let [int] = tp.segments; {
then {
let name = int.ident.name.as_str(); let name = int.ident.name.as_str();
candidates.iter().find(|c| &name == *c).copied() candidates.iter().find(|c| &name == *c).copied()
} else { } else {
None None
} }
}
} }
/// Gets the type as a string, if it is a supported integer /// Gets the type as a string, if it is a supported integer
fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> { fn int_ty_to_sym<'tcx>(path: &QPath<'_>) -> Option<&'tcx str> {
if_chain! { if let QPath::Resolved(_, path) = *path
if let QPath::Resolved(_, path) = *path; && let [ty] = path.segments
if let [ty] = path.segments; {
then {
let name = ty.ident.name.as_str(); let name = ty.ident.name.as_str();
INTS.iter().find(|c| &name == *c).copied() INTS.iter().find(|c| &name == *c).copied()
} else { } else {
None None
} }
}
} }
/// Will return the expressions as if they were expr1 <= expr2 /// Will return the expressions as if they were expr1 <= expr2

View file

@ -15,7 +15,6 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability}; use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability};
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use if_chain::if_chain;
use rustc_ast::ast; use rustc_ast::ast;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass}; use rustc_lint::{EarlyContext, EarlyLintPass};
@ -121,18 +120,21 @@ fn block_starts_with_comment(cx: &EarlyContext<'_>, expr: &ast::Block) -> bool {
} }
fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) { fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_: &ast::Expr) {
if_chain! { if let ast::ExprKind::Block(ref block, _) = else_.kind
if let ast::ExprKind::Block(ref block, _) = else_.kind; && !block_starts_with_comment(cx, block)
if !block_starts_with_comment(cx, block); && let Some(else_) = expr_block(block)
if let Some(else_) = expr_block(block); && else_.attrs.is_empty()
if else_.attrs.is_empty(); && !else_.span.from_expansion()
if !else_.span.from_expansion(); && let ast::ExprKind::If(..) = else_.kind
if let ast::ExprKind::If(..) = else_.kind; {
then {
// Prevent "elseif" // Prevent "elseif"
// Check that the "else" is followed by whitespace // Check that the "else" is followed by whitespace
let up_to_else = then_span.between(block.span); let up_to_else = then_span.between(block.span);
let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() { !c.is_whitespace() } else { false }; let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() {
!c.is_whitespace()
} else {
false
};
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg( span_lint_and_sugg(
@ -149,21 +151,24 @@ fn check_collapsible_maybe_if_let(cx: &EarlyContext<'_>, then_span: Span, else_:
applicability, applicability,
); );
} }
}
} }
fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) { fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &ast::Expr, then: &ast::Block) {
if_chain! { if !block_starts_with_comment(cx, then)
if !block_starts_with_comment(cx, then); && let Some(inner) = expr_block(then)
if let Some(inner) = expr_block(then); && inner.attrs.is_empty()
if inner.attrs.is_empty(); && let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind
if let ast::ExprKind::If(ref check_inner, ref content, None) = inner.kind;
// Prevent triggering on `if c { if let a = b { .. } }`. // Prevent triggering on `if c { if let a = b { .. } }`.
if !matches!(check_inner.kind, ast::ExprKind::Let(..)); && !matches!(check_inner.kind, ast::ExprKind::Let(..))
let ctxt = expr.span.ctxt(); && let ctxt = expr.span.ctxt()
if inner.span.ctxt() == ctxt; && inner.span.ctxt() == ctxt
then { {
span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this `if` statement can be collapsed", |diag| { span_lint_and_then(
cx,
COLLAPSIBLE_IF,
expr.span,
"this `if` statement can be collapsed",
|diag| {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app); let lhs = Sugg::ast(cx, check, "..", ctxt, &mut app);
let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app); let rhs = Sugg::ast(cx, check_inner, "..", ctxt, &mut app);
@ -177,8 +182,8 @@ fn check_collapsible_no_if_let(cx: &EarlyContext<'_>, expr: &ast::Expr, check: &
), ),
app, // snippet app, // snippet
); );
}); },
} );
} }
} }

View file

@ -117,7 +117,7 @@ declare_clippy_lint! {
/// ``` /// ```
#[clippy::version = "pre 1.29.0"] #[clippy::version = "pre 1.29.0"]
pub IF_SAME_THEN_ELSE, pub IF_SAME_THEN_ELSE,
correctness, style,
"`if` with the same `then` and `else` blocks" "`if` with the same `then` and `else` blocks"
} }

View file

@ -5,8 +5,6 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym; use rustc_span::sym;
use if_chain::if_chain;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
/// Checks for types that implement `Copy` as well as /// Checks for types that implement `Copy` as well as
@ -38,16 +36,15 @@ declare_lint_pass!(CopyIterator => [COPY_ITERATOR]);
impl<'tcx> LateLintPass<'tcx> for CopyIterator { impl<'tcx> LateLintPass<'tcx> for CopyIterator {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if_chain! {
if let ItemKind::Impl(Impl { if let ItemKind::Impl(Impl {
of_trait: Some(ref trait_ref), of_trait: Some(ref trait_ref),
.. ..
}) = item.kind; }) = item.kind
let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
if is_copy(cx, ty); && is_copy(cx, ty)
if let Some(trait_id) = trait_ref.trait_def_id(); && let Some(trait_id) = trait_ref.trait_def_id()
if cx.tcx.is_diagnostic_item(sym::Iterator, trait_id); && cx.tcx.is_diagnostic_item(sym::Iterator, trait_id)
then { {
span_lint_and_note( span_lint_and_note(
cx, cx,
COPY_ITERATOR, COPY_ITERATOR,
@ -58,5 +55,4 @@ impl<'tcx> LateLintPass<'tcx> for CopyIterator {
); );
} }
} }
}
} }

View file

@ -53,12 +53,11 @@ declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]);
impl EarlyLintPass for CrateInMacroDef { impl EarlyLintPass for CrateInMacroDef {
fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) { fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
if_chain! { if item.attrs.iter().any(is_macro_export)
if item.attrs.iter().any(is_macro_export); && let ItemKind::MacroDef(macro_def) = &item.kind
if let ItemKind::MacroDef(macro_def) = &item.kind; && let tts = macro_def.body.tokens.clone()
let tts = macro_def.body.tokens.clone(); && let Some(span) = contains_unhygienic_crate_reference(&tts)
if let Some(span) = contains_unhygienic_crate_reference(&tts); {
then {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
CRATE_IN_MACRO_DEF, CRATE_IN_MACRO_DEF,
@ -70,34 +69,29 @@ impl EarlyLintPass for CrateInMacroDef {
); );
} }
} }
}
} }
fn is_macro_export(attr: &Attribute) -> bool { fn is_macro_export(attr: &Attribute) -> bool {
if_chain! { if let AttrKind::Normal(normal) = &attr.kind
if let AttrKind::Normal(normal) = &attr.kind; && let [segment] = normal.item.path.segments.as_slice()
if let [segment] = normal.item.path.segments.as_slice(); {
then {
segment.ident.name == sym::macro_export segment.ident.name == sym::macro_export
} else { } else {
false false
} }
}
} }
fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> { fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> {
let mut prev_is_dollar = false; let mut prev_is_dollar = false;
let mut cursor = tts.trees(); let mut cursor = tts.trees();
while let Some(curr) = cursor.next() { while let Some(curr) = cursor.next() {
if_chain! { if !prev_is_dollar
if !prev_is_dollar; && let Some(span) = is_crate_keyword(curr)
if let Some(span) = is_crate_keyword(curr); && let Some(next) = cursor.look_ahead(0)
if let Some(next) = cursor.look_ahead(0); && is_token(next, &TokenKind::ModSep)
if is_token(next, &TokenKind::ModSep); {
then {
return Some(span); return Some(span);
} }
}
if let TokenTree::Delimited(_, _, tts) = &curr { if let TokenTree::Delimited(_, _, tts) = &curr {
let span = contains_unhygienic_crate_reference(tts); let span = contains_unhygienic_crate_reference(tts);
if span.is_some() { if span.is_some() {
@ -110,10 +104,18 @@ fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> {
} }
fn is_crate_keyword(tt: &TokenTree) -> Option<Span> { fn is_crate_keyword(tt: &TokenTree) -> Option<Span> {
if_chain! { if let TokenTree::Token(
if let TokenTree::Token(Token { kind: TokenKind::Ident(symbol, _), span }, _) = tt; Token {
if symbol.as_str() == "crate"; kind: TokenKind::Ident(symbol, _),
then { Some(*span) } else { None } span,
},
_,
) = tt
&& symbol.as_str() == "crate"
{
Some(*span)
} else {
None
} }
} }

View file

@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -33,12 +32,11 @@ declare_lint_pass!(CreateDir => [CREATE_DIR]);
impl LateLintPass<'_> for CreateDir { impl LateLintPass<'_> for CreateDir {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! { if let ExprKind::Call(func, [arg, ..]) = expr.kind
if let ExprKind::Call(func, [arg, ..]) = expr.kind; && let ExprKind::Path(ref path) = func.kind
if let ExprKind::Path(ref path) = func.kind; && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
if let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id(); && cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id)
if cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id); {
then {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
CREATE_DIR, CREATE_DIR,
@ -47,8 +45,7 @@ impl LateLintPass<'_> for CreateDir {
"consider calling `std::fs::create_dir_all` instead", "consider calling `std::fs::create_dir_all` instead",
format!("create_dir_all({})", snippet(cx, arg.span, "..")), format!("create_dir_all({})", snippet(cx, arg.span, "..")),
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
) );
}
} }
} }
} }

View file

@ -6,7 +6,7 @@ use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Node}; use rustc_hir::{Expr, ExprKind, Node};
use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::{sym, BytePos, Pos, Span}; use rustc_span::sym;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -31,31 +31,6 @@ declare_clippy_lint! {
"`dbg!` macro is intended as a debugging tool" "`dbg!` macro is intended as a debugging tool"
} }
/// Gets the span of the statement up to the next semicolon, if and only if the next
/// non-whitespace character actually is a semicolon.
/// E.g.
/// ```rust,ignore
///
/// dbg!();
/// ^^^^^^^ this span is returned
///
/// foo!(dbg!());
/// no span is returned
/// ```
fn span_including_semi(cx: &LateContext<'_>, span: Span) -> Option<Span> {
let sm = cx.sess().source_map();
let sf = sm.lookup_source_file(span.hi());
let src = sf.src.as_ref()?.get(span.hi().to_usize()..)?;
let first_non_whitespace = src.find(|c: char| !c.is_whitespace())?;
if src.as_bytes()[first_non_whitespace] == b';' {
let hi = span.hi() + BytePos::from_usize(first_non_whitespace + 1);
Some(span.with_hi(hi))
} else {
None
}
}
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct DbgMacro { pub struct DbgMacro {
allow_dbg_in_tests: bool, allow_dbg_in_tests: bool,
@ -88,10 +63,10 @@ impl LateLintPass<'_> for DbgMacro {
ExprKind::Block(..) => { ExprKind::Block(..) => {
// If the `dbg!` macro is a "free" statement and not contained within other expressions, // If the `dbg!` macro is a "free" statement and not contained within other expressions,
// remove the whole statement. // remove the whole statement.
if let Some(Node::Stmt(stmt)) = cx.tcx.hir().find_parent(expr.hir_id) if let Some(Node::Stmt(_)) = cx.tcx.hir().find_parent(expr.hir_id)
&& let Some(span) = span_including_semi(cx, stmt.span.source_callsite()) && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span)
{ {
(span, String::new()) (macro_call.span.to(semi_span), String::new())
} else { } else {
(macro_call.span, String::from("()")) (macro_call.span, String::from("()"))
} }

View file

@ -10,8 +10,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
crate::utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS_INFO, crate::utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS_INFO,
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
crate::utils::internal_lints::if_chain_style::IF_CHAIN_STYLE_INFO,
#[cfg(feature = "internal")]
crate::utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL_INFO, crate::utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL_INFO,
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
crate::utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR_INFO, crate::utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR_INFO,
@ -141,6 +139,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::doc::MISSING_PANICS_DOC_INFO, crate::doc::MISSING_PANICS_DOC_INFO,
crate::doc::MISSING_SAFETY_DOC_INFO, crate::doc::MISSING_SAFETY_DOC_INFO,
crate::doc::NEEDLESS_DOCTEST_MAIN_INFO, crate::doc::NEEDLESS_DOCTEST_MAIN_INFO,
crate::doc::SUSPICIOUS_DOC_COMMENTS_INFO,
crate::doc::UNNECESSARY_SAFETY_DOC_INFO, crate::doc::UNNECESSARY_SAFETY_DOC_INFO,
crate::double_parens::DOUBLE_PARENS_INFO, crate::double_parens::DOUBLE_PARENS_INFO,
crate::drop_forget_ref::DROP_NON_DROP_INFO, crate::drop_forget_ref::DROP_NON_DROP_INFO,
@ -232,6 +231,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO, crate::items_after_statements::ITEMS_AFTER_STATEMENTS_INFO,
crate::items_after_test_module::ITEMS_AFTER_TEST_MODULE_INFO, crate::items_after_test_module::ITEMS_AFTER_TEST_MODULE_INFO,
crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO, crate::iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR_INFO,
crate::iter_over_hash_type::ITER_OVER_HASH_TYPE_INFO,
crate::iter_without_into_iter::INTO_ITER_WITHOUT_ITER_INFO, crate::iter_without_into_iter::INTO_ITER_WITHOUT_ITER_INFO,
crate::iter_without_into_iter::ITER_WITHOUT_INTO_ITER_INFO, crate::iter_without_into_iter::ITER_WITHOUT_INTO_ITER_INFO,
crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO, crate::large_const_arrays::LARGE_CONST_ARRAYS_INFO,
@ -628,7 +628,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
crate::strings::STR_TO_STRING_INFO, crate::strings::STR_TO_STRING_INFO,
crate::strings::TRIM_SPLIT_WHITESPACE_INFO, crate::strings::TRIM_SPLIT_WHITESPACE_INFO,
crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO, crate::strlen_on_c_strings::STRLEN_ON_C_STRINGS_INFO,
crate::suspicious_doc_comments::SUSPICIOUS_DOC_COMMENTS_INFO,
crate::suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS_INFO, crate::suspicious_operation_groupings::SUSPICIOUS_OPERATION_GROUPINGS_INFO,
crate::suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL_INFO, crate::suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL_INFO,
crate::suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL_INFO, crate::suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL_INFO,

View file

@ -2,7 +2,6 @@ use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
use clippy_utils::source::snippet_with_context; use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{has_drop, is_copy}; use clippy_utils::ty::{has_drop, is_copy};
use clippy_utils::{any_parent_is_automatically_derived, contains_name, get_parent_expr, is_from_proc_macro}; use clippy_utils::{any_parent_is_automatically_derived, contains_name, get_parent_expr, is_from_proc_macro};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::Res; use rustc_hir::def::Res;
@ -81,22 +80,21 @@ impl_lint_pass!(Default => [DEFAULT_TRAIT_ACCESS, FIELD_REASSIGN_WITH_DEFAULT]);
impl<'tcx> LateLintPass<'tcx> for Default { impl<'tcx> LateLintPass<'tcx> for Default {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! { if !expr.span.from_expansion()
if !expr.span.from_expansion();
// Avoid cases already linted by `field_reassign_with_default` // Avoid cases already linted by `field_reassign_with_default`
if !self.reassigned_linted.contains(&expr.span); && !self.reassigned_linted.contains(&expr.span)
if let ExprKind::Call(path, ..) = expr.kind; && let ExprKind::Call(path, ..) = expr.kind
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); && !any_parent_is_automatically_derived(cx.tcx, expr.hir_id)
if let ExprKind::Path(ref qpath) = path.kind; && let ExprKind::Path(ref qpath) = path.kind
if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id(); && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
if cx.tcx.is_diagnostic_item(sym::default_fn, def_id); && cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
if !is_update_syntax_base(cx, expr); && !is_update_syntax_base(cx, expr)
// Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type. // Detect and ignore <Foo as Default>::default() because these calls do explicitly name the type.
if let QPath::Resolved(None, _path) = qpath; && let QPath::Resolved(None, _path) = qpath
let expr_ty = cx.typeck_results().expr_ty(expr); && let expr_ty = cx.typeck_results().expr_ty(expr)
if let ty::Adt(def, ..) = expr_ty.kind(); && let ty::Adt(def, ..) = expr_ty.kind()
if !is_from_proc_macro(cx, expr); && !is_from_proc_macro(cx, expr)
then { {
let replacement = with_forced_trimmed_paths!(format!("{}::default()", cx.tcx.def_path_str(def.did()))); let replacement = with_forced_trimmed_paths!(format!("{}::default()", cx.tcx.def_path_str(def.did())));
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
@ -109,7 +107,6 @@ impl<'tcx> LateLintPass<'tcx> for Default {
); );
} }
} }
}
#[expect(clippy::too_many_lines)] #[expect(clippy::too_many_lines)]
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
@ -124,38 +121,36 @@ impl<'tcx> LateLintPass<'tcx> for Default {
// find all binding statements like `let mut _ = T::default()` where `T::default()` is the // find all binding statements like `let mut _ = T::default()` where `T::default()` is the
// `default` method of the `Default` trait, and store statement index in current block being // `default` method of the `Default` trait, and store statement index in current block being
// checked and the name of the bound variable // checked and the name of the bound variable
let (local, variant, binding_name, binding_type, span) = if_chain! { let (local, variant, binding_name, binding_type, span) = if let StmtKind::Local(local) = stmt.kind
// only take `let ...` statements // only take `let ...` statements
if let StmtKind::Local(local) = stmt.kind; && let Some(expr) = local.init
if let Some(expr) = local.init; && !any_parent_is_automatically_derived(cx.tcx, expr.hir_id)
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id); && !expr.span.from_expansion()
if !expr.span.from_expansion();
// only take bindings to identifiers // only take bindings to identifiers
if let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind; && let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind
// only when assigning `... = Default::default()` // only when assigning `... = Default::default()`
if is_expr_default(expr, cx); && is_expr_default(expr, cx)
let binding_type = cx.typeck_results().node_type(binding_id); && let binding_type = cx.typeck_results().node_type(binding_id)
if let Some(adt) = binding_type.ty_adt_def(); && let Some(adt) = binding_type.ty_adt_def()
if adt.is_struct(); && adt.is_struct()
let variant = adt.non_enum_variant(); && let variant = adt.non_enum_variant()
if adt.did().is_local() || !variant.is_field_list_non_exhaustive(); && (adt.did().is_local() || !variant.is_field_list_non_exhaustive())
let module_did = cx.tcx.parent_module(stmt.hir_id); && let module_did = cx.tcx.parent_module(stmt.hir_id)
if variant && variant
.fields .fields
.iter() .iter()
.all(|field| field.vis.is_accessible_from(module_did, cx.tcx)); .all(|field| field.vis.is_accessible_from(module_did, cx.tcx))
let all_fields_are_copy = variant && let all_fields_are_copy = variant
.fields .fields
.iter() .iter()
.all(|field| { .all(|field| {
is_copy(cx, cx.tcx.type_of(field.did).instantiate_identity()) is_copy(cx, cx.tcx.type_of(field.did).instantiate_identity())
}); })
if !has_drop(cx, binding_type) || all_fields_are_copy; && (!has_drop(cx, binding_type) || all_fields_are_copy)
then { {
(local, variant, ident.name, binding_type, expr.span) (local, variant, ident.name, binding_type, expr.span)
} else { } else {
continue; continue;
}
}; };
let init_ctxt = local.span.ctxt(); let init_ctxt = local.span.ctxt();
@ -216,10 +211,9 @@ impl<'tcx> LateLintPass<'tcx> for Default {
.join(", "); .join(", ");
// give correct suggestion if generics are involved (see #6944) // give correct suggestion if generics are involved (see #6944)
let binding_type = if_chain! { let binding_type = if let ty::Adt(adt_def, args) = binding_type.kind()
if let ty::Adt(adt_def, args) = binding_type.kind(); && !args.is_empty()
if !args.is_empty(); {
then {
let adt_def_ty_name = cx.tcx.item_name(adt_def.did()); let adt_def_ty_name = cx.tcx.item_name(adt_def.did());
let generic_args = args.iter().collect::<Vec<_>>(); let generic_args = args.iter().collect::<Vec<_>>();
let tys_str = generic_args let tys_str = generic_args
@ -230,7 +224,6 @@ impl<'tcx> LateLintPass<'tcx> for Default {
format!("{adt_def_ty_name}::<{}>", &tys_str) format!("{adt_def_ty_name}::<{}>", &tys_str)
} else { } else {
binding_type.to_string() binding_type.to_string()
}
}; };
let sugg = if ext_with_default { let sugg = if ext_with_default {
@ -260,48 +253,42 @@ impl<'tcx> LateLintPass<'tcx> for Default {
/// Checks if the given expression is the `default` method belonging to the `Default` trait. /// Checks if the given expression is the `default` method belonging to the `Default` trait.
fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool { fn is_expr_default<'tcx>(expr: &'tcx Expr<'tcx>, cx: &LateContext<'tcx>) -> bool {
if_chain! { if let ExprKind::Call(fn_expr, _) = &expr.kind
if let ExprKind::Call(fn_expr, _) = &expr.kind; && let ExprKind::Path(qpath) = &fn_expr.kind
if let ExprKind::Path(qpath) = &fn_expr.kind; && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id); {
then {
// right hand side of assignment is `Default::default` // right hand side of assignment is `Default::default`
cx.tcx.is_diagnostic_item(sym::default_fn, def_id) cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
} else { } else {
false false
} }
}
} }
/// Returns the reassigned field and the assigning expression (right-hand side of assign). /// Returns the reassigned field and the assigning expression (right-hand side of assign).
fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> { fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Option<(Ident, &'tcx Expr<'tcx>)> {
if_chain! { if let StmtKind::Semi(later_expr) = this.kind
// only take assignments // only take assignments
if let StmtKind::Semi(later_expr) = this.kind; && let ExprKind::Assign(assign_lhs, assign_rhs, _) = later_expr.kind
if let ExprKind::Assign(assign_lhs, assign_rhs, _) = later_expr.kind;
// only take assignments to fields where the left-hand side field is a field of // only take assignments to fields where the left-hand side field is a field of
// the same binding as the previous statement // the same binding as the previous statement
if let ExprKind::Field(binding, field_ident) = assign_lhs.kind; && let ExprKind::Field(binding, field_ident) = assign_lhs.kind
if let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind; && let ExprKind::Path(QPath::Resolved(_, path)) = binding.kind
if let Some(second_binding_name) = path.segments.last(); && let Some(second_binding_name) = path.segments.last()
if second_binding_name.ident.name == binding_name; && second_binding_name.ident.name == binding_name
then { {
Some((field_ident, assign_rhs)) Some((field_ident, assign_rhs))
} else { } else {
None None
} }
}
} }
/// Returns whether `expr` is the update syntax base: `Foo { a: 1, .. base }` /// Returns whether `expr` is the update syntax base: `Foo { a: 1, .. base }`
fn is_update_syntax_base<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool { fn is_update_syntax_base<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
if_chain! { if let Some(parent) = get_parent_expr(cx, expr)
if let Some(parent) = get_parent_expr(cx, expr); && let ExprKind::Struct(_, _, Some(base)) = parent.kind
if let ExprKind::Struct(_, _, Some(base)) = parent.kind; {
then {
base.hir_id == expr.hir_id base.hir_id == expr.hir_id
} else { } else {
false false
} }
}
} }

View file

@ -56,22 +56,21 @@ fn is_alias(ty: hir::Ty<'_>) -> bool {
impl LateLintPass<'_> for DefaultConstructedUnitStructs { impl LateLintPass<'_> for DefaultConstructedUnitStructs {
fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) {
if_chain!( if let hir::ExprKind::Call(fn_expr, &[]) = expr.kind
// make sure we have a call to `Default::default` // make sure we have a call to `Default::default`
if let hir::ExprKind::Call(fn_expr, &[]) = expr.kind; && let ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(base, _)) = fn_expr.kind
if let ExprKind::Path(ref qpath @ hir::QPath::TypeRelative(base, _)) = fn_expr.kind;
// make sure this isn't a type alias: // make sure this isn't a type alias:
// `<Foo as Bar>::Assoc` cannot be used as a constructor // `<Foo as Bar>::Assoc` cannot be used as a constructor
if !is_alias(*base); && !is_alias(*base)
if let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id); && let Res::Def(_, def_id) = cx.qpath_res(qpath, fn_expr.hir_id)
if cx.tcx.is_diagnostic_item(sym::default_fn, def_id); && cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
// make sure we have a struct with no fields (unit struct) // make sure we have a struct with no fields (unit struct)
if let ty::Adt(def, ..) = cx.typeck_results().expr_ty(expr).kind(); && let ty::Adt(def, ..) = cx.typeck_results().expr_ty(expr).kind()
if def.is_struct(); && def.is_struct()
if let var @ ty::VariantDef { ctor: Some((hir::def::CtorKind::Const, _)), .. } = def.non_enum_variant(); && let var @ ty::VariantDef { ctor: Some((hir::def::CtorKind::Const, _)), .. } = def.non_enum_variant()
if !var.is_field_list_non_exhaustive(); && !var.is_field_list_non_exhaustive()
if !expr.span.from_expansion() && !qpath.span().from_expansion(); && !expr.span.from_expansion() && !qpath.span().from_expansion()
then { {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
DEFAULT_CONSTRUCTED_UNIT_STRUCTS, DEFAULT_CONSTRUCTED_UNIT_STRUCTS,
@ -80,8 +79,7 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs {
"remove this call to `default`", "remove this call to `default`",
String::new(), String::new(),
Applicability::MachineApplicable, Applicability::MachineApplicable,
)
}
); );
};
} }
} }

View file

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::{get_parent_node, numeric_literal}; use clippy_utils::{get_parent_node, numeric_literal};
use if_chain::if_chain;
use rustc_ast::ast::{LitFloatType, LitIntType, LitKind}; use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor}; use rustc_hir::intravisit::{walk_expr, walk_stmt, Visitor};
@ -82,12 +81,13 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
/// Check whether a passed literal has potential to cause fallback or not. /// Check whether a passed literal has potential to cause fallback or not.
fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) { fn check_lit(&self, lit: &Lit, lit_ty: Ty<'tcx>, emit_hir_id: HirId) {
if_chain! { if !in_external_macro(self.cx.sess(), lit.span)
if !in_external_macro(self.cx.sess(), lit.span); && matches!(self.ty_bounds.last(), Some(ExplicitTyBound(false)))
if matches!(self.ty_bounds.last(), Some(ExplicitTyBound(false))); && matches!(
if matches!(lit.node, lit.node,
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)); LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)
then { )
{
let (suffix, is_float) = match lit_ty.kind() { let (suffix, is_float) = match lit_ty.kind() {
ty::Int(IntTy::I32) => ("i32", false), ty::Int(IntTy::I32) => ("i32", false),
ty::Float(FloatTy::F64) => ("f64", true), ty::Float(FloatTy::F64) => ("f64", true),
@ -113,11 +113,10 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
"default numeric fallback might occur", "default numeric fallback might occur",
|diag| { |diag| {
diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect); diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect);
} },
); );
} }
} }
}
} }
impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
@ -149,22 +148,20 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
ExprKind::Struct(_, fields, base) => { ExprKind::Struct(_, fields, base) => {
let ty = self.cx.typeck_results().expr_ty(expr); let ty = self.cx.typeck_results().expr_ty(expr);
if_chain! { if let Some(adt_def) = ty.ty_adt_def()
if let Some(adt_def) = ty.ty_adt_def(); && adt_def.is_struct()
if adt_def.is_struct(); && let Some(variant) = adt_def.variants().iter().next()
if let Some(variant) = adt_def.variants().iter().next(); {
then {
let fields_def = &variant.fields; let fields_def = &variant.fields;
// Push field type then visit each field expr. // Push field type then visit each field expr.
for field in *fields { for field in *fields {
let bound = let bound = fields_def.iter().find_map(|f_def| {
fields_def if f_def.ident(self.cx.tcx) == field.ident {
.iter() Some(self.cx.tcx.type_of(f_def.did).instantiate_identity())
.find_map(|f_def| { } else {
if f_def.ident(self.cx.tcx) == field.ident None
{ Some(self.cx.tcx.type_of(f_def.did).instantiate_identity()) } }
else { None }
}); });
self.ty_bounds.push(bound.into()); self.ty_bounds.push(bound.into());
self.visit_expr(field.expr); self.visit_expr(field.expr);
@ -179,7 +176,6 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
} }
return; return;
} }
}
}, },
ExprKind::Lit(lit) => { ExprKind::Lit(lit) => {

View file

@ -1,10 +1,11 @@
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; 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::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::sugg::has_enclosing_paren;
use clippy_utils::ty::{implements_trait, peel_mid_ty_refs}; use clippy_utils::ty::{implements_trait, is_manually_drop, peel_mid_ty_refs};
use clippy_utils::{ use clippy_utils::{
expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, get_parent_node, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode,
}; };
use core::mem;
use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX}; use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::fx::FxIndexMap;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -170,9 +171,7 @@ pub struct Dereferencing<'tcx> {
#[derive(Debug)] #[derive(Debug)]
struct StateData<'tcx> { struct StateData<'tcx> {
/// Span of the top level expression first_expr: &'tcx Expr<'tcx>,
span: Span,
hir_id: HirId,
adjusted_ty: Ty<'tcx>, adjusted_ty: Ty<'tcx>,
} }
@ -198,6 +197,7 @@ enum State {
}, },
ExplicitDerefField { ExplicitDerefField {
name: Symbol, name: Symbol,
derefs_manually_drop: bool,
}, },
Reborrow { Reborrow {
mutability: Mutability, mutability: Mutability,
@ -242,7 +242,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
// Stop processing sub expressions when a macro call is seen // Stop processing sub expressions when a macro call is seen
if expr.span.from_expansion() { if expr.span.from_expansion() {
if let Some((state, data)) = self.state.take() { if let Some((state, data)) = self.state.take() {
report(cx, expr, state, data); report(cx, expr, state, data, cx.typeck_results());
} }
return; return;
} }
@ -251,7 +251,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
let Some((kind, sub_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else { let Some((kind, sub_expr)) = try_parse_ref_op(cx.tcx, typeck, expr) else {
// The whole chain of reference operations has been seen // The whole chain of reference operations has been seen
if let Some((state, data)) = self.state.take() { if let Some((state, data)) = self.state.take() {
report(cx, expr, state, data); report(cx, expr, state, data, typeck);
} }
return; return;
}; };
@ -272,14 +272,16 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
(Some(use_cx), RefOp::Deref) => { (Some(use_cx), RefOp::Deref) => {
let sub_ty = typeck.expr_ty(sub_expr); let sub_ty = typeck.expr_ty(sub_expr);
if let ExprUseNode::FieldAccess(name) = use_cx.node if let ExprUseNode::FieldAccess(name) = use_cx.node
&& adjusted_ty.ty_adt_def().map_or(true, |adt| !adt.is_union()) && !use_cx.moved_before_use
&& !ty_contains_field(sub_ty, name.name) && !ty_contains_field(sub_ty, name.name)
{ {
self.state = Some(( self.state = Some((
State::ExplicitDerefField { name: name.name }, State::ExplicitDerefField {
name: name.name,
derefs_manually_drop: is_manually_drop(sub_ty),
},
StateData { StateData {
span: expr.span, first_expr: expr,
hir_id: expr.hir_id,
adjusted_ty, adjusted_ty,
}, },
)); ));
@ -293,8 +295,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
self.state = Some(( self.state = Some((
State::ExplicitDeref { mutability: None }, State::ExplicitDeref { mutability: None },
StateData { StateData {
span: expr.span, first_expr: expr,
hir_id: expr.hir_id,
adjusted_ty, adjusted_ty,
}, },
)); ));
@ -313,8 +314,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
mutbl, mutbl,
}, },
StateData { StateData {
span: expr.span, first_expr: expr,
hir_id: expr.hir_id,
adjusted_ty, adjusted_ty,
}, },
)); ));
@ -342,8 +342,18 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
TyCoercionStability::for_defined_ty(cx, ty, use_cx.node.is_return()) TyCoercionStability::for_defined_ty(cx, ty, use_cx.node.is_return())
}); });
let can_auto_borrow = match use_cx.node { let can_auto_borrow = match use_cx.node {
ExprUseNode::Callee => true, ExprUseNode::FieldAccess(_)
ExprUseNode::FieldAccess(_) => adjusted_ty.ty_adt_def().map_or(true, |adt| !adt.is_union()), if !use_cx.moved_before_use && matches!(sub_expr.kind, ExprKind::Field(..)) =>
{
// `DerefMut` will not be automatically applied to `ManuallyDrop<_>`
// field expressions when the base type is a union and the parent
// expression is also a field access.
//
// e.g. `&mut x.y.z` where `x` is a union, and accessing `z` requires a
// deref through `ManuallyDrop<_>` will not compile.
!adjust_derefs_manually_drop(use_cx.adjustments, expr_ty)
},
ExprUseNode::Callee | ExprUseNode::FieldAccess(_) => true,
ExprUseNode::MethodArg(hir_id, _, 0) if !use_cx.moved_before_use => { ExprUseNode::MethodArg(hir_id, _, 0) if !use_cx.moved_before_use => {
// Check for calls to trait methods where the trait is implemented // Check for calls to trait methods where the trait is implemented
// on a reference. // on a reference.
@ -357,11 +367,8 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
.tcx .tcx
.erase_regions(use_cx.adjustments.last().map_or(expr_ty, |a| a.target)) .erase_regions(use_cx.adjustments.last().map_or(expr_ty, |a| a.target))
&& let ty::Ref(_, sub_ty, _) = *arg_ty.kind() && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
&& let args = cx && let args =
.typeck_results() typeck.node_args_opt(hir_id).map(|args| &args[1..]).unwrap_or_default()
.node_args_opt(hir_id)
.map(|args| &args[1..])
.unwrap_or_default()
&& let impl_ty = && let impl_ty =
if cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder().inputs()[0] if cx.tcx.fn_sig(fn_id).instantiate_identity().skip_binder().inputs()[0]
.is_ref() .is_ref()
@ -436,14 +443,16 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
count: deref_count - required_refs, count: deref_count - required_refs,
msg, msg,
stability, stability,
for_field_access: match use_cx.node { for_field_access: if let ExprUseNode::FieldAccess(name) = use_cx.node
ExprUseNode::FieldAccess(name) => Some(name.name), && !use_cx.moved_before_use
_ => None, {
Some(name.name)
} else {
None
}, },
}), }),
StateData { StateData {
span: expr.span, first_expr: expr,
hir_id: expr.hir_id,
adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target), adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target),
}, },
)); ));
@ -455,8 +464,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
self.state = Some(( self.state = Some((
State::Borrow { mutability }, State::Borrow { mutability },
StateData { StateData {
span: expr.span, first_expr: expr,
hir_id: expr.hir_id,
adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target), adjusted_ty: use_cx.adjustments.last().map_or(expr_ty, |a| a.target),
}, },
)); ));
@ -501,13 +509,12 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
(Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => { (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf(mutability)) => {
let adjusted_ty = data.adjusted_ty; let adjusted_ty = data.adjusted_ty;
let stability = state.stability; let stability = state.stability;
report(cx, expr, State::DerefedBorrow(state), data); report(cx, expr, State::DerefedBorrow(state), data, typeck);
if stability.is_deref_stable() { if stability.is_deref_stable() {
self.state = Some(( self.state = Some((
State::Borrow { mutability }, State::Borrow { mutability },
StateData { StateData {
span: expr.span, first_expr: expr,
hir_id: expr.hir_id,
adjusted_ty, adjusted_ty,
}, },
)); ));
@ -517,15 +524,18 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
let adjusted_ty = data.adjusted_ty; let adjusted_ty = data.adjusted_ty;
let stability = state.stability; let stability = state.stability;
let for_field_access = state.for_field_access; let for_field_access = state.for_field_access;
report(cx, expr, State::DerefedBorrow(state), data); report(cx, expr, State::DerefedBorrow(state), data, typeck);
if let Some(name) = for_field_access if let Some(name) = for_field_access
&& !ty_contains_field(typeck.expr_ty(sub_expr), name) && let sub_expr_ty = typeck.expr_ty(sub_expr)
&& !ty_contains_field(sub_expr_ty, name)
{ {
self.state = Some(( self.state = Some((
State::ExplicitDerefField { name }, State::ExplicitDerefField {
name,
derefs_manually_drop: is_manually_drop(sub_expr_ty),
},
StateData { StateData {
span: expr.span, first_expr: expr,
hir_id: expr.hir_id,
adjusted_ty, adjusted_ty,
}, },
)); ));
@ -535,8 +545,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
self.state = Some(( self.state = Some((
State::ExplicitDeref { mutability: None }, State::ExplicitDeref { mutability: None },
StateData { StateData {
span: parent.span, first_expr: parent,
hir_id: parent.hir_id,
adjusted_ty, adjusted_ty,
}, },
)); ));
@ -566,13 +575,28 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
(state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => { (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => {
self.state = state; self.state = state;
}, },
(Some((State::ExplicitDerefField { name }, data)), RefOp::Deref) (
if !ty_contains_field(typeck.expr_ty(sub_expr), name) => Some((
State::ExplicitDerefField {
name,
derefs_manually_drop,
},
data,
)),
RefOp::Deref,
) if let sub_expr_ty = typeck.expr_ty(sub_expr)
&& !ty_contains_field(sub_expr_ty, name) =>
{ {
self.state = Some((State::ExplicitDerefField { name }, data)); self.state = Some((
State::ExplicitDerefField {
name,
derefs_manually_drop: derefs_manually_drop || is_manually_drop(sub_expr_ty),
},
data,
));
}, },
(Some((state, data)), _) => report(cx, expr, state, data), (Some((state, data)), _) => report(cx, expr, state, data, typeck),
} }
} }
@ -597,12 +621,11 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
return; return;
} }
if_chain! { if !pat.span.from_expansion()
if !pat.span.from_expansion(); && let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind()
if let ty::Ref(_, tam, _) = *cx.typeck_results().pat_ty(pat).kind();
// only lint immutable refs, because borrowed `&mut T` cannot be moved out // only lint immutable refs, because borrowed `&mut T` cannot be moved out
if let ty::Ref(_, _, Mutability::Not) = *tam.kind(); && let ty::Ref(_, _, Mutability::Not) = *tam.kind()
then { {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0; let snip = snippet_with_context(cx, name.span, pat.span.ctxt(), "..", &mut app).0;
self.current_body = self.current_body.or(cx.enclosing_body); self.current_body = self.current_body.or(cx.enclosing_body);
@ -619,7 +642,6 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
} }
} }
} }
}
fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) { fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &'tcx Body<'_>) {
if Some(body.id()) == self.current_body { if Some(body.id()) == self.current_body {
@ -689,6 +711,14 @@ fn try_parse_ref_op<'tcx>(
} }
} }
// Checks if the adjustments contains a deref of `ManuallyDrop<_>`
fn adjust_derefs_manually_drop<'tcx>(adjustments: &'tcx [Adjustment<'tcx>], mut ty: Ty<'tcx>) -> bool {
adjustments.iter().any(|a| {
let ty = mem::replace(&mut ty, a.target);
matches!(a.kind, Adjust::Deref(Some(ref op)) if op.mutbl == Mutability::Mut) && is_manually_drop(ty)
})
}
// Checks whether the type for a deref call actually changed the type, not just the mutability of // Checks whether the type for a deref call actually changed the type, not just the mutability of
// the reference. // the reference.
fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool { fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
@ -898,7 +928,13 @@ fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
} }
#[expect(clippy::needless_pass_by_value, clippy::too_many_lines)] #[expect(clippy::needless_pass_by_value, clippy::too_many_lines)]
fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData<'tcx>) { fn report<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
state: State,
data: StateData<'tcx>,
typeck: &'tcx TypeckResults<'tcx>,
) {
match state { match state {
State::DerefMethod { State::DerefMethod {
ty_changed_count, ty_changed_count,
@ -906,8 +942,9 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
mutbl, mutbl,
} => { } => {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let (expr_str, _expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); let (expr_str, _expr_is_macro_call) =
let ty = cx.typeck_results().expr_ty(expr); snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app);
let ty = typeck.expr_ty(expr);
let (_, ref_count) = peel_mid_ty_refs(ty); let (_, ref_count) = peel_mid_ty_refs(ty);
let deref_str = if ty_changed_count >= ref_count && ref_count != 0 { let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
// a deref call changing &T -> &U requires two deref operators the first time // a deref call changing &T -> &U requires two deref operators the first time
@ -947,7 +984,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
EXPLICIT_DEREF_METHODS, EXPLICIT_DEREF_METHODS,
data.span, data.first_expr.span,
match mutbl { match mutbl {
Mutability::Not => "explicit `deref` method call", Mutability::Not => "explicit `deref` method call",
Mutability::Mut => "explicit `deref_mut` method call", Mutability::Mut => "explicit `deref_mut` method call",
@ -959,11 +996,18 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
}, },
State::DerefedBorrow(state) => { State::DerefedBorrow(state) => {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); let (snip, snip_is_macro) =
span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| { snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app);
let (precedence, calls_field) = match get_parent_node(cx.tcx, data.hir_id) { span_lint_hir_and_then(
cx,
NEEDLESS_BORROW,
data.first_expr.hir_id,
data.first_expr.span,
state.msg,
|diag| {
let (precedence, calls_field) = match get_parent_node(cx.tcx, data.first_expr.hir_id) {
Some(Node::Expr(e)) => match e.kind { Some(Node::Expr(e)) => match e.kind {
ExprKind::Call(callee, _) if callee.hir_id != data.hir_id => (0, false), ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => (0, false),
ExprKind::Call(..) => (PREC_POSTFIX, matches!(expr.kind, ExprKind::Field(..))), ExprKind::Call(..) => (PREC_POSTFIX, matches!(expr.kind, ExprKind::Field(..))),
_ => (e.precedence().order(), false), _ => (e.precedence().order(), false),
}, },
@ -977,8 +1021,9 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
} else { } else {
snip.into() snip.into()
}; };
diag.span_suggestion(data.span, "change this to", sugg, app); diag.span_suggestion(data.first_expr.span, "change this to", sugg, app);
}); },
);
}, },
State::ExplicitDeref { mutability } => { State::ExplicitDeref { mutability } => {
if matches!( if matches!(
@ -996,7 +1041,7 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
} }
let (prefix, precedence) = if let Some(mutability) = mutability let (prefix, precedence) = if let Some(mutability) = mutability
&& !cx.typeck_results().expr_ty(expr).is_ref() && !typeck.expr_ty(expr).is_ref()
{ {
let prefix = match mutability { let prefix = match mutability {
Mutability::Not => "&", Mutability::Not => "&",
@ -1009,53 +1054,61 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data
span_lint_hir_and_then( span_lint_hir_and_then(
cx, cx,
EXPLICIT_AUTO_DEREF, EXPLICIT_AUTO_DEREF,
data.hir_id, data.first_expr.hir_id,
data.span, data.first_expr.span,
"deref which would be done by auto-deref", "deref which would be done by auto-deref",
|diag| { |diag| {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app); let (snip, snip_is_macro) =
snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app);
let sugg = let sugg =
if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) { if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
format!("{prefix}({snip})") format!("{prefix}({snip})")
} else { } else {
format!("{prefix}{snip}") format!("{prefix}{snip}")
}; };
diag.span_suggestion(data.span, "try", sugg, app); diag.span_suggestion(data.first_expr.span, "try", sugg, app);
}, },
); );
}, },
State::ExplicitDerefField { .. } => { State::ExplicitDerefField {
if matches!( derefs_manually_drop, ..
expr.kind, } => {
ExprKind::Block(..) let (snip_span, needs_parens) = if matches!(expr.kind, ExprKind::Field(..))
| ExprKind::ConstBlock(_) && (derefs_manually_drop
| ExprKind::If(..) || adjust_derefs_manually_drop(
| ExprKind::Loop(..) typeck.expr_adjustments(data.first_expr),
| ExprKind::Match(..) typeck.expr_ty(data.first_expr),
) && data.adjusted_ty.is_sized(cx.tcx, cx.param_env) )) {
{ // `DerefMut` will not be automatically applied to `ManuallyDrop<_>`
// Rustc bug: auto deref doesn't work on block expression when targeting sized types. // field expressions when the base type is a union and the parent
return; // expression is also a field access.
} //
// e.g. `&mut x.y.z` where `x` is a union, and accessing `z` requires a
if let ExprKind::Field(parent_expr, _) = expr.kind // deref through `ManuallyDrop<_>` will not compile.
&& let ty::Adt(adt, _) = cx.typeck_results().expr_ty(parent_expr).kind() let parent_id = cx.tcx.hir().parent_id(expr.hir_id);
&& adt.is_union() if parent_id == data.first_expr.hir_id {
{
// Auto deref does not apply on union field
return; return;
} }
(cx.tcx.hir().get(parent_id).expect_expr().span, true)
} else {
(expr.span, false)
};
span_lint_hir_and_then( span_lint_hir_and_then(
cx, cx,
EXPLICIT_AUTO_DEREF, EXPLICIT_AUTO_DEREF,
data.hir_id, data.first_expr.hir_id,
data.span, data.first_expr.span,
"deref which would be done by auto-deref", "deref which would be done by auto-deref",
|diag| { |diag| {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0; let snip = snippet_with_context(cx, snip_span, data.first_expr.span.ctxt(), "..", &mut app).0;
diag.span_suggestion(data.span, "try", snip.into_owned(), app); let sugg = if needs_parens {
format!("({snip})")
} else {
snip.into_owned()
};
diag.span_suggestion(data.first_expr.span, "try", sugg, app);
}, },
); );
}, },

View file

@ -148,78 +148,61 @@ fn check_struct<'tcx>(
} }
fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>) { fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>) {
if_chain! { if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind
if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind; && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res; && let variant_id = cx.tcx.parent(id)
if let variant_id = cx.tcx.parent(id); && let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id)
if let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id); && variant_def.fields.is_empty()
if variant_def.fields.is_empty(); && !variant_def.is_field_list_non_exhaustive()
if !variant_def.is_field_list_non_exhaustive(); {
then {
let enum_span = cx.tcx.def_span(adt_def.did()); let enum_span = cx.tcx.def_span(adt_def.did());
let indent_enum = indent_of(cx, enum_span).unwrap_or(0); let indent_enum = indent_of(cx, enum_span).unwrap_or(0);
let variant_span = cx.tcx.def_span(variant_def.def_id); let variant_span = cx.tcx.def_span(variant_def.def_id);
let indent_variant = indent_of(cx, variant_span).unwrap_or(0); let indent_variant = indent_of(cx, variant_span).unwrap_or(0);
span_lint_and_then( span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| {
cx,
DERIVABLE_IMPLS,
item.span,
"this `impl` can be derived",
|diag| {
diag.span_suggestion_hidden( diag.span_suggestion_hidden(
item.span, item.span,
"remove the manual implementation...", "remove the manual implementation...",
String::new(), String::new(),
Applicability::MachineApplicable Applicability::MachineApplicable,
); );
diag.span_suggestion( diag.span_suggestion(
enum_span.shrink_to_lo(), enum_span.shrink_to_lo(),
"...and instead derive it...", "...and instead derive it...",
format!( format!("#[derive(Default)]\n{indent}", indent = " ".repeat(indent_enum),),
"#[derive(Default)]\n{indent}", Applicability::MachineApplicable,
indent = " ".repeat(indent_enum),
),
Applicability::MachineApplicable
); );
diag.span_suggestion( diag.span_suggestion(
variant_span.shrink_to_lo(), variant_span.shrink_to_lo(),
"...and mark the default variant", "...and mark the default variant",
format!( format!("#[default]\n{indent}", indent = " ".repeat(indent_variant),),
"#[default]\n{indent}", Applicability::MachineApplicable,
indent = " ".repeat(indent_variant),
),
Applicability::MachineApplicable
); );
} });
);
}
} }
} }
impl<'tcx> LateLintPass<'tcx> for DerivableImpls { impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if_chain! {
if let ItemKind::Impl(Impl { if let ItemKind::Impl(Impl {
of_trait: Some(ref trait_ref), of_trait: Some(ref trait_ref),
items: [child], items: [child],
self_ty, self_ty,
.. ..
}) = item.kind; }) = item.kind
if !cx.tcx.has_attr(item.owner_id, sym::automatically_derived); && !cx.tcx.has_attr(item.owner_id, sym::automatically_derived)
if !item.span.from_expansion(); && !item.span.from_expansion()
if let Some(def_id) = trait_ref.trait_def_id(); && let Some(def_id) = trait_ref.trait_def_id()
if cx.tcx.is_diagnostic_item(sym::Default, def_id); && cx.tcx.is_diagnostic_item(sym::Default, def_id)
if let impl_item_hir = child.id.hir_id(); && let impl_item_hir = child.id.hir_id()
if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); && let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir)
if let ImplItemKind::Fn(_, b) = &impl_item.kind; && let ImplItemKind::Fn(_, b) = &impl_item.kind
if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); && let Body { value: func_expr, .. } = cx.tcx.hir().body(*b)
if let &Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind(); && let &Adt(adt_def, args) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind()
if let attrs = cx.tcx.hir().attrs(item.hir_id()); && let attrs = cx.tcx.hir().attrs(item.hir_id())
if !attrs.iter().any(|attr| attr.doc_str().is_some()); && !attrs.iter().any(|attr| attr.doc_str().is_some())
if cx.tcx.hir().attrs(impl_item_hir).is_empty(); && cx.tcx.hir().attrs(impl_item_hir).is_empty()
{
then {
if adt_def.is_struct() { if adt_def.is_struct() {
check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b)); check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b));
} else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) { } else if adt_def.is_enum() && self.msrv.meets(msrvs::DEFAULT_ENUM_ATTRIBUTE) {
@ -227,7 +210,6 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
} }
} }
} }
}
extract_msrv_attr!(LateContext); extract_msrv_attr!(LateContext);
} }

View file

@ -1,7 +1,6 @@
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_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy}; use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
use clippy_utils::{is_lint_allowed, match_def_path, paths}; use clippy_utils::{is_lint_allowed, match_def_path, paths};
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor}; use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
@ -232,11 +231,10 @@ fn check_hash_peq<'tcx>(
ty: Ty<'tcx>, ty: Ty<'tcx>,
hash_is_automatically_derived: bool, hash_is_automatically_derived: bool,
) { ) {
if_chain! { if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait()
if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait(); && let Some(def_id) = trait_ref.trait_def_id()
if let Some(def_id) = trait_ref.trait_def_id(); && cx.tcx.is_diagnostic_item(sym::Hash, def_id)
if cx.tcx.is_diagnostic_item(sym::Hash, def_id); {
then {
// Look for the PartialEq implementations for `ty` // Look for the PartialEq implementations for `ty`
cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| { cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); let peq_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
@ -258,17 +256,13 @@ fn check_hash_peq<'tcx>(
|diag| { |diag| {
if let Some(local_def_id) = impl_id.as_local() { if let Some(local_def_id) = impl_id.as_local() {
let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
diag.span_note( diag.span_note(cx.tcx.hir().span(hir_id), "`PartialEq` implemented here");
cx.tcx.hir().span(hir_id),
"`PartialEq` implemented here"
);
}
} }
},
); );
} }
}); });
} }
}
} }
/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint. /// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
@ -279,12 +273,11 @@ fn check_ord_partial_ord<'tcx>(
ty: Ty<'tcx>, ty: Ty<'tcx>,
ord_is_automatically_derived: bool, ord_is_automatically_derived: bool,
) { ) {
if_chain! { if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord)
if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord); && let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait()
if let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait(); && let Some(def_id) = &trait_ref.trait_def_id()
if let Some(def_id) = &trait_ref.trait_def_id(); && *def_id == ord_trait_def_id
if *def_id == ord_trait_def_id; {
then {
// Look for the PartialOrd implementations for `ty` // Look for the PartialOrd implementations for `ty`
cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| { cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived); let partial_ord_is_automatically_derived = cx.tcx.has_attr(impl_id, sym::automatically_derived);
@ -304,24 +297,14 @@ fn check_ord_partial_ord<'tcx>(
"you are deriving `Ord` but have implemented `PartialOrd` explicitly" "you are deriving `Ord` but have implemented `PartialOrd` explicitly"
}; };
span_lint_and_then( span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| {
cx,
DERIVE_ORD_XOR_PARTIAL_ORD,
span,
mess,
|diag| {
if let Some(local_def_id) = impl_id.as_local() { if let Some(local_def_id) = impl_id.as_local() {
let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); let hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id);
diag.span_note( diag.span_note(cx.tcx.hir().span(hir_id), "`PartialOrd` implemented here");
cx.tcx.hir().span(hir_id),
"`PartialOrd` implemented here"
);
}
}
);
} }
}); });
} }
});
} }
} }
@ -394,28 +377,28 @@ fn check_unsafe_derive_deserialize<'tcx>(
visitor.has_unsafe visitor.has_unsafe
} }
if_chain! { if let Some(trait_def_id) = trait_ref.trait_def_id()
if let Some(trait_def_id) = trait_ref.trait_def_id(); && match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE)
if match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE); && let ty::Adt(def, _) = ty.kind()
if let ty::Adt(def, _) = ty.kind(); && let Some(local_def_id) = def.did().as_local()
if let Some(local_def_id) = def.did().as_local(); && let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id)
let adt_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_def_id); && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id)
if !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id); && cx
if cx.tcx.inherent_impls(def.did()) .tcx
.inherent_impls(def.did())
.iter() .iter()
.map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local())) .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local()))
.any(|imp| has_unsafe(cx, imp)); .any(|imp| has_unsafe(cx, imp))
then { {
span_lint_and_help( span_lint_and_help(
cx, cx,
UNSAFE_DERIVE_DESERIALIZE, UNSAFE_DERIVE_DESERIALIZE,
item.span, item.span,
"you are deriving `serde::Deserialize` on a type that has methods using `unsafe`", "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
None, None,
"consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html" "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html",
); );
} }
}
} }
struct UnsafeVisitor<'a, 'tcx> { struct UnsafeVisitor<'a, 'tcx> {
@ -431,13 +414,11 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
return; return;
} }
if_chain! { if let Some(header) = kind.header()
if let Some(header) = kind.header(); && header.unsafety == Unsafety::Unsafe
if header.unsafety == Unsafety::Unsafe; {
then {
self.has_unsafe = true; self.has_unsafe = true;
} }
}
walk_fn(self, kind, decl, body_id, id); walk_fn(self, kind, decl, body_id, id);
} }
@ -463,20 +444,19 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint. /// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) { fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
if_chain! { if let ty::Adt(adt, args) = ty.kind()
if let ty::Adt(adt, args) = ty.kind(); && cx.tcx.visibility(adt.did()).is_public()
if cx.tcx.visibility(adt.did()).is_public(); && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq)
if let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq); && let Some(def_id) = trait_ref.trait_def_id()
if let Some(def_id) = trait_ref.trait_def_id(); && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id)
if cx.tcx.is_diagnostic_item(sym::PartialEq, def_id); && let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id)
let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id); && !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[])
if !implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]);
// If all of our fields implement `Eq`, we can implement `Eq` too // If all of our fields implement `Eq`, we can implement `Eq` too
if adt && adt
.all_fields() .all_fields()
.map(|f| f.ty(cx.tcx, args)) .map(|f| f.ty(cx.tcx, args))
.all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[])); .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, &[]))
then { {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
DERIVE_PARTIAL_EQ_WITHOUT_EQ, DERIVE_PARTIAL_EQ_WITHOUT_EQ,
@ -485,8 +465,7 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r
"consider deriving `Eq` as well", "consider deriving `Eq` as well",
"PartialEq, Eq".to_string(), "PartialEq, Eq".to_string(),
Applicability::MachineApplicable, Applicability::MachineApplicable,
) );
}
} }
} }

View file

@ -31,9 +31,9 @@ pub struct DisallowedNames {
} }
impl DisallowedNames { impl DisallowedNames {
pub fn new(disallow: FxHashSet<String>) -> Self { pub fn new(disallowed_names: &[String]) -> Self {
Self { Self {
disallow, disallow: disallowed_names.iter().cloned().collect(),
test_modules_deep: 0, test_modules_deep: 0,
} }
} }

View file

@ -4,13 +4,14 @@ use clippy_utils::macros::{is_panic, root_macro_call_first_node};
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty}; use clippy_utils::{is_entrypoint_fn, method_chain_args, return_ty};
use if_chain::if_chain;
use pulldown_cmark::Event::{ use pulldown_cmark::Event::{
Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text, Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
}; };
use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph}; use pulldown_cmark::Tag::{CodeBlock, Heading, Item, Link, Paragraph};
use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options}; use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options};
use rustc_ast::ast::{Async, Attribute, Fn, FnRetTy, ItemKind}; use rustc_ast::ast::{Async, Attribute, Fn, FnRetTy, ItemKind};
use rustc_ast::token::CommentKind;
use rustc_ast::{AttrKind, AttrStyle};
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sync::Lrc; use rustc_data_structures::sync::Lrc;
use rustc_errors::emitter::EmitterWriter; use rustc_errors::emitter::EmitterWriter;
@ -30,8 +31,8 @@ use rustc_resolve::rustdoc::{
use rustc_session::parse::ParseSess; use rustc_session::parse::ParseSess;
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::edition::Edition; use rustc_span::edition::Edition;
use rustc_span::{sym, BytePos, FileName, Pos, Span};
use rustc_span::source_map::{FilePathMapping, SourceMap}; use rustc_span::source_map::{FilePathMapping, SourceMap};
use rustc_span::{sym, BytePos, FileName, Pos, Span};
use std::ops::Range; use std::ops::Range;
use std::{io, thread}; use std::{io, thread};
use url::Url; use url::Url;
@ -261,6 +262,53 @@ declare_clippy_lint! {
"`pub fn` or `pub trait` with `# Safety` docs" "`pub fn` or `pub trait` with `# Safety` docs"
} }
declare_clippy_lint! {
/// ### What it does
/// Detects the use of outer doc comments (`///`, `/**`) followed by a bang (`!`): `///!`
///
/// ### Why is this bad?
/// Triple-slash comments (known as "outer doc comments") apply to items that follow it.
/// An outer doc comment followed by a bang (i.e. `///!`) has no specific meaning.
///
/// The user most likely meant to write an inner doc comment (`//!`, `/*!`), which
/// applies to the parent item (i.e. the item that the comment is contained in,
/// usually a module or crate).
///
/// ### Known problems
/// Inner doc comments can only appear before items, so there are certain cases where the suggestion
/// made by this lint is not valid code. For example:
/// ```rs
/// fn foo() {}
/// ///!
/// fn bar() {}
/// ```
/// This lint detects the doc comment and suggests changing it to `//!`, but an inner doc comment
/// is not valid at that position.
///
/// ### Example
/// In this example, the doc comment is attached to the *function*, rather than the *module*.
/// ```no_run
/// pub mod util {
/// ///! This module contains utility functions.
///
/// pub fn dummy() {}
/// }
/// ```
///
/// Use instead:
/// ```no_run
/// pub mod util {
/// //! This module contains utility functions.
///
/// pub fn dummy() {}
/// }
/// ```
#[clippy::version = "1.70.0"]
pub SUSPICIOUS_DOC_COMMENTS,
suspicious,
"suspicious usage of (outer) doc comments"
}
#[expect(clippy::module_name_repetitions)] #[expect(clippy::module_name_repetitions)]
#[derive(Clone)] #[derive(Clone)]
pub struct DocMarkdown { pub struct DocMarkdown {
@ -269,9 +317,9 @@ pub struct DocMarkdown {
} }
impl DocMarkdown { impl DocMarkdown {
pub fn new(valid_idents: FxHashSet<String>) -> Self { pub fn new(valid_idents: &[String]) -> Self {
Self { Self {
valid_idents, valid_idents: valid_idents.iter().cloned().collect(),
in_trait_impl: false, in_trait_impl: false,
} }
} }
@ -285,6 +333,7 @@ impl_lint_pass!(DocMarkdown => [
MISSING_PANICS_DOC, MISSING_PANICS_DOC,
NEEDLESS_DOCTEST_MAIN, NEEDLESS_DOCTEST_MAIN,
UNNECESSARY_SAFETY_DOC, UNNECESSARY_SAFETY_DOC,
SUSPICIOUS_DOC_COMMENTS
]); ]);
impl<'tcx> LateLintPass<'tcx> for DocMarkdown { impl<'tcx> LateLintPass<'tcx> for DocMarkdown {
@ -428,17 +477,15 @@ fn lint_for_missing_headers(
span, span,
"docs for function returning `Result` missing `# Errors` section", "docs for function returning `Result` missing `# Errors` section",
); );
} else { } else if let Some(body_id) = body_id
if_chain! { && let Some(future) = cx.tcx.lang_items().future_trait()
if let Some(body_id) = body_id; && let typeck = cx.tcx.typeck_body(body_id)
if let Some(future) = cx.tcx.lang_items().future_trait(); && let body = cx.tcx.hir().body(body_id)
let typeck = cx.tcx.typeck_body(body_id); && let ret_ty = typeck.expr_ty(body.value)
let body = cx.tcx.hir().body(body_id); && implements_trait(cx, ret_ty, future, &[])
let ret_ty = typeck.expr_ty(body.value); && let ty::Coroutine(_, subs, _) = ret_ty.kind()
if implements_trait(cx, ret_ty, future, &[]); && is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result)
if let ty::Coroutine(_, subs, _) = ret_ty.kind(); {
if is_type_diagnostic_item(cx, subs.as_coroutine().return_ty(), sym::Result);
then {
span_lint( span_lint(
cx, cx,
MISSING_ERRORS_DOC, MISSING_ERRORS_DOC,
@ -447,8 +494,6 @@ fn lint_for_missing_headers(
); );
} }
} }
}
}
} }
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -483,6 +528,8 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
return None; return None;
} }
check_almost_inner_doc(cx, attrs);
let (fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true); let (fragments, _) = attrs_to_doc_fragments(attrs.iter().map(|attr| (attr, None)), true);
let mut doc = String::new(); let mut doc = String::new();
for fragment in &fragments { for fragment in &fragments {
@ -511,6 +558,43 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
)) ))
} }
/// Looks for `///!` and `/**!` comments, which were probably meant to be `//!` and `/*!`
fn check_almost_inner_doc(cx: &LateContext<'_>, attrs: &[Attribute]) {
let replacements: Vec<_> = attrs
.iter()
.filter_map(|attr| {
if let AttrKind::DocComment(com_kind, sym) = attr.kind
&& let AttrStyle::Outer = attr.style
&& let Some(com) = sym.as_str().strip_prefix('!')
{
let sugg = match com_kind {
CommentKind::Line => format!("//!{com}"),
CommentKind::Block => format!("/*!{com}*/"),
};
Some((attr.span, sugg))
} else {
None
}
})
.collect();
if let Some((&(lo_span, _), &(hi_span, _))) = replacements.first().zip(replacements.last()) {
span_lint_and_then(
cx,
SUSPICIOUS_DOC_COMMENTS,
lo_span.to(hi_span),
"this is an outer doc comment and does not apply to the parent module or crate",
|diag| {
diag.multipart_suggestion(
"use an inner doc comment to document the parent module or crate",
replacements,
Applicability::MaybeIncorrect,
);
},
);
}
}
const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"]; const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
#[allow(clippy::too_many_lines)] // Only a big match statement #[allow(clippy::too_many_lines)] // Only a big match statement
@ -649,11 +733,12 @@ fn check_code(cx: &LateContext<'_>, text: &str, edition: Edition, range: Range<u
rustc_span::create_session_globals_then(edition, || { rustc_span::create_session_globals_then(edition, || {
let filename = FileName::anon_source_code(&code); let filename = FileName::anon_source_code(&code);
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
let fallback_bundle = let fallback_bundle =
rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false); rustc_errors::fallback_fluent_bundle(rustc_driver::DEFAULT_LOCALE_RESOURCES.to_vec(), false);
let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle); let emitter = EmitterWriter::new(Box::new(io::sink()), fallback_bundle);
let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings(); let handler = Handler::with_emitter(Box::new(emitter)).disable_warnings();
#[expect(clippy::arc_with_non_send_sync)] // `Lrc` is expected by with_span_handler
let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
let sess = ParseSess::with_span_handler(handler, sm); let sess = ParseSess::with_span_handler(handler, sm);
let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) { let mut parser = match maybe_new_parser_from_source_str(&sess, filename, code) {

View file

@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::peel_blocks; use clippy_utils::peel_blocks;
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node}; use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -36,21 +35,21 @@ declare_lint_pass!(EmptyDrop => [EMPTY_DROP]);
impl LateLintPass<'_> for EmptyDrop { impl LateLintPass<'_> for EmptyDrop {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if_chain! {
if let ItemKind::Impl(Impl { if let ItemKind::Impl(Impl {
of_trait: Some(ref trait_ref), of_trait: Some(ref trait_ref),
items: [child], items: [child],
.. ..
}) = item.kind; }) = item.kind
if trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait(); && trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait()
if let impl_item_hir = child.id.hir_id(); && let impl_item_hir = child.id.hir_id()
if let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir); && let Some(Node::ImplItem(impl_item)) = cx.tcx.hir().find(impl_item_hir)
if let ImplItemKind::Fn(_, b) = &impl_item.kind; && let ImplItemKind::Fn(_, b) = &impl_item.kind
if let Body { value: func_expr, .. } = cx.tcx.hir().body(*b); && let Body { value: func_expr, .. } = cx.tcx.hir().body(*b)
let func_expr = peel_blocks(func_expr); && let func_expr = peel_blocks(func_expr)
if let ExprKind::Block(block, _) = func_expr.kind; && let ExprKind::Block(block, _) = func_expr.kind
if block.stmts.is_empty() && block.expr.is_none(); && block.stmts.is_empty()
then { && block.expr.is_none()
{
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
EMPTY_DROP, EMPTY_DROP,
@ -58,9 +57,8 @@ impl LateLintPass<'_> for EmptyDrop {
"empty drop implementation", "empty drop implementation",
"try removing this impl", "try removing this impl",
String::new(), String::new(),
Applicability::MaybeIncorrect Applicability::MaybeIncorrect,
); );
} }
} }
}
} }

View file

@ -114,29 +114,25 @@ impl LateLintPass<'_> for EndianBytes {
return; return;
} }
if_chain! { if let ExprKind::MethodCall(method_name, receiver, args, ..) = expr.kind
if let ExprKind::MethodCall(method_name, receiver, args, ..) = expr.kind; && args.is_empty()
if args.is_empty(); && let ty = cx.typeck_results().expr_ty(receiver)
let ty = cx.typeck_results().expr_ty(receiver); && ty.is_primitive_ty()
if ty.is_primitive_ty(); && maybe_lint_endian_bytes(cx, expr, Prefix::To, method_name.ident.name, ty)
if maybe_lint_endian_bytes(cx, expr, Prefix::To, method_name.ident.name, ty); {
then {
return; return;
} }
}
if_chain! { if let ExprKind::Call(function, ..) = expr.kind
if let ExprKind::Call(function, ..) = expr.kind; && let ExprKind::Path(qpath) = function.kind
if let ExprKind::Path(qpath) = function.kind; && let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id()
if let Some(def_id) = cx.qpath_res(&qpath, function.hir_id).opt_def_id(); && let Some(function_name) = cx.get_def_path(def_id).last()
if let Some(function_name) = cx.get_def_path(def_id).last(); && let ty = cx.typeck_results().expr_ty(expr)
let ty = cx.typeck_results().expr_ty(expr); && ty.is_primitive_ty()
if ty.is_primitive_ty(); {
then {
maybe_lint_endian_bytes(cx, expr, Prefix::From, *function_name, ty); maybe_lint_endian_bytes(cx, expr, Prefix::From, *function_name, ty);
} }
} }
}
} }
fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix, name: Symbol, ty: Ty<'_>) -> bool { fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix, name: Symbol, ty: Ty<'_>) -> bool {

View file

@ -8,8 +8,8 @@ use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::{self, TraitRef, Ty}; use rustc_middle::ty::{self, TraitRef, Ty};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::def_id::LocalDefId; use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
use rustc_span::symbol::kw; use rustc_span::symbol::kw;
use rustc_span::Span;
use rustc_target::spec::abi::Abi; use rustc_target::spec::abi::Abi;
#[derive(Copy, Clone)] #[derive(Copy, Clone)]

View file

@ -247,8 +247,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs<'tcx>, call_sig:
/// This is needed because rustc is unable to late bind early-bound regions in a function signature. /// This is needed because rustc is unable to late bind early-bound regions in a function signature.
fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<'_>) -> bool { fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<'_>) -> bool {
fn check_region(from_region: Region<'_>, to_region: Region<'_>) -> bool { fn check_region(from_region: Region<'_>, to_region: Region<'_>) -> bool {
matches!(from_region.kind(), RegionKind::ReBound(..)) matches!(from_region.kind(), RegionKind::ReBound(..)) && !matches!(to_region.kind(), RegionKind::ReBound(..))
&& !matches!(to_region.kind(), RegionKind::ReBound(..))
} }
fn check_subs(from_subs: &[GenericArg<'_>], to_subs: &[GenericArg<'_>]) -> bool { fn check_subs(from_subs: &[GenericArg<'_>], to_subs: &[GenericArg<'_>]) -> bool {

View file

@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::indent_of; use clippy_utils::source::indent_of;
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind}; use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -71,16 +70,13 @@ declare_lint_pass!(ExhaustiveItems => [EXHAUSTIVE_ENUMS, EXHAUSTIVE_STRUCTS]);
impl LateLintPass<'_> for ExhaustiveItems { impl LateLintPass<'_> for ExhaustiveItems {
fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) { fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
if_chain! { if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind
if let ItemKind::Enum(..) | ItemKind::Struct(..) = item.kind; && cx.effective_visibilities.is_exported(item.owner_id.def_id)
if cx.effective_visibilities.is_exported(item.owner_id.def_id); && let attrs = cx.tcx.hir().attrs(item.hir_id())
let attrs = cx.tcx.hir().attrs(item.hir_id()); && !attrs.iter().any(|a| a.has_name(sym::non_exhaustive))
if !attrs.iter().any(|a| a.has_name(sym::non_exhaustive)); {
then {
let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind { let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind {
if v.fields().iter().any(|f| { if v.fields().iter().any(|f| !cx.tcx.visibility(f.def_id).is_public()) {
!cx.tcx.visibility(f.def_id).is_public()
}) {
// skip structs with private fields // skip structs with private fields
return; return;
} }
@ -90,21 +86,15 @@ impl LateLintPass<'_> for ExhaustiveItems {
}; };
let suggestion_span = item.span.shrink_to_lo(); let suggestion_span = item.span.shrink_to_lo();
let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0)); let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0));
span_lint_and_then( span_lint_and_then(cx, lint, item.span, msg, |diag| {
cx,
lint,
item.span,
msg,
|diag| {
let sugg = format!("#[non_exhaustive]\n{indent}"); let sugg = format!("#[non_exhaustive]\n{indent}");
diag.span_suggestion(suggestion_span, diag.span_suggestion(
suggestion_span,
"try adding #[non_exhaustive]", "try adding #[non_exhaustive]",
sugg, sugg,
Applicability::MaybeIncorrect); Applicability::MaybeIncorrect,
}
); );
});
}
} }
} }
} }

View file

@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint; use clippy_utils::diagnostics::span_lint;
use clippy_utils::is_entrypoint_fn; use clippy_utils::is_entrypoint_fn;
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node}; use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
@ -42,19 +41,17 @@ declare_lint_pass!(Exit => [EXIT]);
impl<'tcx> LateLintPass<'tcx> for Exit { impl<'tcx> LateLintPass<'tcx> for Exit {
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
if_chain! { if let ExprKind::Call(path_expr, _args) = e.kind
if let ExprKind::Call(path_expr, _args) = e.kind; && let ExprKind::Path(ref path) = path_expr.kind
if let ExprKind::Path(ref path) = path_expr.kind; && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id()
if let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id(); && cx.tcx.is_diagnostic_item(sym::process_exit, def_id)
if cx.tcx.is_diagnostic_item(sym::process_exit, def_id); && let parent = cx.tcx.hir().get_parent_item(e.hir_id).def_id
let parent = cx.tcx.hir().get_parent_item(e.hir_id).def_id; && let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find_by_def_id(parent)
if let Some(Node::Item(Item{kind: ItemKind::Fn(..), ..})) = cx.tcx.hir().find_by_def_id(parent);
// If the next item up is a function we check if it is an entry point // If the next item up is a function we check if it is an entry point
// and only then emit a linter warning // and only then emit a linter warning
if !is_entrypoint_fn(cx, parent.to_def_id()); && !is_entrypoint_fn(cx, parent.to_def_id())
then { {
span_lint(cx, EXIT, e.span, "usage of `process::exit`"); span_lint(cx, EXIT, e.span, "usage of `process::exit`");
} }
} }
}
} }

View file

@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::macros::{find_format_args, format_args_inputs_span}; use clippy_utils::macros::{find_format_args, format_args_inputs_span};
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{is_expn_of, path_def_id}; use clippy_utils::{is_expn_of, path_def_id};
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::Res; use rustc_hir::def::Res;
use rustc_hir::{BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind}; use rustc_hir::{BindingAnnotation, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind};
@ -101,30 +100,28 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
/// If `kind` is a block that looks like `{ let result = $expr; result }` then /// If `kind` is a block that looks like `{ let result = $expr; result }` then
/// returns $expr. Otherwise returns `kind`. /// returns $expr. Otherwise returns `kind`.
fn look_in_block<'tcx, 'hir>(cx: &LateContext<'tcx>, kind: &'tcx ExprKind<'hir>) -> &'tcx ExprKind<'hir> { fn look_in_block<'tcx, 'hir>(cx: &LateContext<'tcx>, kind: &'tcx ExprKind<'hir>) -> &'tcx ExprKind<'hir> {
if_chain! { if let ExprKind::Block(block, _label @ None) = kind
if let ExprKind::Block(block, _label @ None) = kind; && let Block {
if let Block {
stmts: [Stmt { kind: StmtKind::Local(local), .. }], stmts: [Stmt { kind: StmtKind::Local(local), .. }],
expr: Some(expr_end_of_block), expr: Some(expr_end_of_block),
rules: BlockCheckMode::DefaultBlock, rules: BlockCheckMode::DefaultBlock,
.. ..
} = block; } = block
// Find id of the local that expr_end_of_block resolves to // Find id of the local that expr_end_of_block resolves to
if let ExprKind::Path(QPath::Resolved(None, expr_path)) = expr_end_of_block.kind; && let ExprKind::Path(QPath::Resolved(None, expr_path)) = expr_end_of_block.kind
if let Res::Local(expr_res) = expr_path.res; && let Res::Local(expr_res) = expr_path.res
if let Some(Node::Pat(res_pat)) = cx.tcx.hir().find(expr_res); && let Some(Node::Pat(res_pat)) = cx.tcx.hir().find(expr_res)
// Find id of the local we found in the block // Find id of the local we found in the block
if let PatKind::Binding(BindingAnnotation::NONE, local_hir_id, _ident, None) = local.pat.kind; && let PatKind::Binding(BindingAnnotation::NONE, local_hir_id, _ident, None) = local.pat.kind
// If those two are the same hir id // If those two are the same hir id
if res_pat.hir_id == local_hir_id; && res_pat.hir_id == local_hir_id
if let Some(init) = local.init; && let Some(init) = local.init
then { {
return &init.kind; return &init.kind;
} }
}
kind kind
} }

View file

@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::{is_panic, root_macro_call_first_node}; use clippy_utils::macros::{is_panic, root_macro_call_first_node};
use clippy_utils::method_chain_args; use clippy_utils::method_chain_args;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty; use rustc_middle::ty;
@ -53,15 +52,15 @@ declare_lint_pass!(FallibleImplFrom => [FALLIBLE_IMPL_FROM]);
impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom { impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
// check for `impl From<???> for ..` // check for `impl From<???> for ..`
if_chain! { if let hir::ItemKind::Impl(impl_) = &item.kind
if let hir::ItemKind::Impl(impl_) = &item.kind; && let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id)
if let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id); && cx
if cx.tcx.is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id); .tcx
then { .is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id)
{
lint_impl_body(cx, item.span, impl_.items); lint_impl_body(cx, item.span, impl_.items);
} }
} }
}
} }
fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::ImplItemRef]) { fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::ImplItemRef]) {
@ -98,11 +97,9 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl
} }
for impl_item in impl_items { for impl_item in impl_items {
if_chain! { if impl_item.ident.name == sym::from
if impl_item.ident.name == sym::from; && let ImplItemKind::Fn(_, body_id) = cx.tcx.hir().impl_item(impl_item.id).kind
if let ImplItemKind::Fn(_, body_id) = {
cx.tcx.hir().impl_item(impl_item.id).kind;
then {
// check the body for `begin_panic` or `unwrap` // check the body for `begin_panic` or `unwrap`
let body = cx.tcx.hir().body(body_id); let body = cx.tcx.hir().body(body_id);
let mut fpu = FindPanicUnwrap { let mut fpu = FindPanicUnwrap {
@ -122,10 +119,11 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl
move |diag| { move |diag| {
diag.help( diag.help(
"`From` is intended for infallible conversions only. \ "`From` is intended for infallible conversions only. \
Use `TryFrom` if there's a possibility for the conversion to fail"); Use `TryFrom` if there's a possibility for the conversion to fail",
);
diag.span_note(fpu.result, "potential failure(s)"); diag.span_note(fpu.result, "potential failure(s)");
}); },
} );
} }
} }
} }

View file

@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::numeric_literal; use clippy_utils::numeric_literal;
use if_chain::if_chain;
use rustc_ast::ast::{self, LitFloatType, LitKind}; use rustc_ast::ast::{self, LitFloatType, LitKind};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
@ -64,11 +63,10 @@ declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]);
impl<'tcx> LateLintPass<'tcx> for FloatLiteral { impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
let ty = cx.typeck_results().expr_ty(expr); let ty = cx.typeck_results().expr_ty(expr);
if_chain! { if let ty::Float(fty) = *ty.kind()
if let ty::Float(fty) = *ty.kind(); && let hir::ExprKind::Lit(lit) = expr.kind
if let hir::ExprKind::Lit(lit) = expr.kind; && let LitKind::Float(sym, lit_float_ty) = lit.node
if let LitKind::Float(sym, lit_float_ty) = lit.node; {
then {
let sym_str = sym.as_str(); let sym_str = sym.as_str();
let formatter = FloatFormat::new(sym_str); let formatter = FloatFormat::new(sym_str);
// Try to bail out if the float is for sure fine. // Try to bail out if the float is for sure fine.
@ -80,7 +78,7 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
let type_suffix = match lit_float_ty { let type_suffix = match lit_float_ty {
LitFloatType::Suffixed(ast::FloatTy::F32) => Some("f32"), LitFloatType::Suffixed(ast::FloatTy::F32) => Some("f32"),
LitFloatType::Suffixed(ast::FloatTy::F64) => Some("f64"), LitFloatType::Suffixed(ast::FloatTy::F64) => Some("f64"),
LitFloatType::Unsuffixed => None LitFloatType::Unsuffixed => None,
}; };
let (is_whole, is_inf, mut float_str) = match fty { let (is_whole, is_inf, mut float_str) = match fty {
FloatTy::F32 => { FloatTy::F32 => {
@ -91,7 +89,6 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
FloatTy::F64 => { FloatTy::F64 => {
let value = sym_str.parse::<f64>().unwrap(); let value = sym_str.parse::<f64>().unwrap();
(value.fract() == 0.0, value.is_infinite(), formatter.format(value)) (value.fract() == 0.0, value.is_infinite(), formatter.format(value))
}, },
}; };
@ -133,7 +130,6 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
} }
} }
} }
}
} }
#[must_use] #[must_use]

View file

@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::{ use clippy_utils::{
eq_expr_value, get_parent_expr, higher, in_constant, is_no_std_crate, numeric_literal, peel_blocks, sugg, eq_expr_value, get_parent_expr, higher, in_constant, is_no_std_crate, numeric_literal, peel_blocks, sugg,
}; };
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp}; use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -133,31 +132,26 @@ fn prepare_receiver_sugg<'a>(cx: &LateContext<'_>, mut expr: &'a Expr<'a>) -> Su
expr = inner_expr; expr = inner_expr;
} }
if_chain! { if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind()
// if the expression is a float literal and it is unsuffixed then // if the expression is a float literal and it is unsuffixed then
// add a suffix so the suggestion is valid and unambiguous // add a suffix so the suggestion is valid and unambiguous
if let ty::Float(float_ty) = cx.typeck_results().expr_ty(expr).kind(); && let ExprKind::Lit(lit) = &expr.kind
if let ExprKind::Lit(lit) = &expr.kind; && let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node
if let ast::LitKind::Float(sym, ast::LitFloatType::Unsuffixed) = lit.node; {
then {
let op = format!( let op = format!(
"{suggestion}{}{}", "{suggestion}{}{}",
// Check for float literals without numbers following the decimal // Check for float literals without numbers following the decimal
// separator such as `2.` and adds a trailing zero // separator such as `2.` and adds a trailing zero
if sym.as_str().ends_with('.') { if sym.as_str().ends_with('.') { "0" } else { "" },
"0"
} else {
""
},
float_ty.name_str() float_ty.name_str()
).into(); )
.into();
suggestion = match suggestion { suggestion = match suggestion {
Sugg::MaybeParen(_) => Sugg::MaybeParen(op), Sugg::MaybeParen(_) => Sugg::MaybeParen(op),
_ => Sugg::NonParen(op) _ => Sugg::NonParen(op),
}; };
} }
}
suggestion.maybe_par() suggestion.maybe_par()
} }
@ -359,35 +353,59 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option<String> {
) = receiver.kind ) = receiver.kind
{ {
// check if expression of the form x * x + y * y // check if expression of the form x * x + y * y
if_chain! { if let ExprKind::Binary(
if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lmul_lhs, lmul_rhs) = add_lhs.kind; Spanned {
if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, rmul_lhs, rmul_rhs) = add_rhs.kind; node: BinOpKind::Mul, ..
if eq_expr_value(cx, lmul_lhs, lmul_rhs); },
if eq_expr_value(cx, rmul_lhs, rmul_rhs); lmul_lhs,
then { lmul_rhs,
return Some(format!("{}.hypot({})", Sugg::hir(cx, lmul_lhs, "..").maybe_par(), Sugg::hir(cx, rmul_lhs, ".."))); ) = add_lhs.kind
} && let ExprKind::Binary(
Spanned {
node: BinOpKind::Mul, ..
},
rmul_lhs,
rmul_rhs,
) = add_rhs.kind
&& eq_expr_value(cx, lmul_lhs, lmul_rhs)
&& eq_expr_value(cx, rmul_lhs, rmul_rhs)
{
return Some(format!(
"{}.hypot({})",
Sugg::hir(cx, lmul_lhs, "..").maybe_par(),
Sugg::hir(cx, rmul_lhs, "..")
));
} }
// check if expression of the form x.powi(2) + y.powi(2) // check if expression of the form x.powi(2) + y.powi(2)
if_chain! {
if let ExprKind::MethodCall( if let ExprKind::MethodCall(
PathSegment { ident: lmethod_name, .. }, PathSegment {
largs_0, [largs_1, ..], ident: lmethod_name, ..
_ },
) = &add_lhs.kind; largs_0,
if let ExprKind::MethodCall( [largs_1, ..],
PathSegment { ident: rmethod_name, .. }, _,
rargs_0, [rargs_1, ..], ) = &add_lhs.kind
_ && let ExprKind::MethodCall(
) = &add_rhs.kind; PathSegment {
if lmethod_name.as_str() == "powi" && rmethod_name.as_str() == "powi"; ident: rmethod_name, ..
if let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1); },
if let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1); rargs_0,
if Int(2) == lvalue && Int(2) == rvalue; [rargs_1, ..],
then { _,
return Some(format!("{}.hypot({})", Sugg::hir(cx, largs_0, "..").maybe_par(), Sugg::hir(cx, rargs_0, ".."))); ) = &add_rhs.kind
} && lmethod_name.as_str() == "powi"
&& rmethod_name.as_str() == "powi"
&& let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1)
&& let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1)
&& Int(2) == lvalue
&& Int(2) == rvalue
{
return Some(format!(
"{}.hypot({})",
Sugg::hir(cx, largs_0, "..").maybe_par(),
Sugg::hir(cx, rargs_0, "..")
));
} }
} }
@ -411,40 +429,45 @@ fn check_hypot(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
// TODO: Lint expressions of the form `x.exp() - y` where y > 1 // TODO: Lint expressions of the form `x.exp() - y` where y > 1
// and suggest usage of `x.exp_m1() - (y - 1)` instead // and suggest usage of `x.exp_m1() - (y - 1)` instead
fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! { if let ExprKind::Binary(
if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, lhs, rhs) = expr.kind; Spanned {
if cx.typeck_results().expr_ty(lhs).is_floating_point(); node: BinOpKind::Sub, ..
if let Some(value) = constant(cx, cx.typeck_results(), rhs); },
if F32(1.0) == value || F64(1.0) == value; lhs,
if let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind; rhs,
if cx.typeck_results().expr_ty(self_arg).is_floating_point(); ) = expr.kind
if path.ident.name.as_str() == "exp"; && cx.typeck_results().expr_ty(lhs).is_floating_point()
then { && let Some(value) = constant(cx, cx.typeck_results(), rhs)
&& (F32(1.0) == value || F64(1.0) == value)
&& let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind
&& cx.typeck_results().expr_ty(self_arg).is_floating_point()
&& path.ident.name.as_str() == "exp"
{
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
IMPRECISE_FLOPS, IMPRECISE_FLOPS,
expr.span, expr.span,
"(e.pow(x) - 1) can be computed more accurately", "(e.pow(x) - 1) can be computed more accurately",
"consider using", "consider using",
format!( format!("{}.exp_m1()", Sugg::hir(cx, self_arg, "..").maybe_par()),
"{}.exp_m1()",
Sugg::hir(cx, self_arg, "..").maybe_par()
),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }
}
} }
fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> { fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'a Expr<'a>, &'a Expr<'a>)> {
if_chain! { if let ExprKind::Binary(
if let ExprKind::Binary(Spanned { node: BinOpKind::Mul, .. }, lhs, rhs) = &expr.kind; Spanned {
if cx.typeck_results().expr_ty(lhs).is_floating_point(); node: BinOpKind::Mul, ..
if cx.typeck_results().expr_ty(rhs).is_floating_point(); },
then { lhs,
rhs,
) = &expr.kind
&& cx.typeck_results().expr_ty(lhs).is_floating_point()
&& cx.typeck_results().expr_ty(rhs).is_floating_point()
{
return Some((lhs, rhs)); return Some((lhs, rhs));
} }
}
None None
} }
@ -553,12 +576,15 @@ fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a
} }
fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! { if let Some(higher::If {
if let Some(higher::If { cond, then, r#else: Some(r#else) }) = higher::If::hir(expr); cond,
let if_body_expr = peel_blocks(then); then,
let else_body_expr = peel_blocks(r#else); r#else: Some(r#else),
if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr); }) = higher::If::hir(expr)
then { && let if_body_expr = peel_blocks(then)
&& let else_body_expr = peel_blocks(r#else)
&& let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr)
{
let positive_abs_sugg = ( let positive_abs_sugg = (
"manual implementation of `abs` method", "manual implementation of `abs` method",
format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()), format!("{}.abs()", Sugg::hir(cx, body, "..").maybe_par()),
@ -592,21 +618,30 @@ fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }
}
} }
fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool { fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>) -> bool {
if_chain! { if let ExprKind::MethodCall(
if let ExprKind::MethodCall(PathSegment { ident: method_name_a, .. }, _, args_a, _) = expr_a.kind; PathSegment {
if let ExprKind::MethodCall(PathSegment { ident: method_name_b, .. }, _, args_b, _) = expr_b.kind; ident: method_name_a, ..
then { },
return method_name_a.as_str() == method_name_b.as_str() && _,
args_a.len() == args_b.len() && args_a,
( _,
["ln", "log2", "log10"].contains(&method_name_a.as_str()) || ) = expr_a.kind
method_name_a.as_str() == "log" && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0]) && let ExprKind::MethodCall(
); PathSegment {
} ident: method_name_b, ..
},
_,
args_b,
_,
) = expr_b.kind
{
return method_name_a.as_str() == method_name_b.as_str()
&& args_a.len() == args_b.len()
&& (["ln", "log2", "log10"].contains(&method_name_a.as_str())
|| method_name_a.as_str() == "log" && args_a.len() == 1 && eq_expr_value(cx, &args_a[0], &args_b[0]));
} }
false false
@ -614,67 +649,66 @@ fn are_same_base_logs(cx: &LateContext<'_>, expr_a: &Expr<'_>, expr_b: &Expr<'_>
fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_log_division(cx: &LateContext<'_>, expr: &Expr<'_>) {
// check if expression of the form x.logN() / y.logN() // check if expression of the form x.logN() / y.logN()
if_chain! {
if let ExprKind::Binary( if let ExprKind::Binary(
Spanned { Spanned {
node: BinOpKind::Div, .. node: BinOpKind::Div, ..
}, },
lhs, lhs,
rhs, rhs,
) = &expr.kind; ) = &expr.kind
if are_same_base_logs(cx, lhs, rhs); && are_same_base_logs(cx, lhs, rhs)
if let ExprKind::MethodCall(_, largs_self, ..) = &lhs.kind; && let ExprKind::MethodCall(_, largs_self, ..) = &lhs.kind
if let ExprKind::MethodCall(_, rargs_self, ..) = &rhs.kind; && let ExprKind::MethodCall(_, rargs_self, ..) = &rhs.kind
then { {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
SUBOPTIMAL_FLOPS, SUBOPTIMAL_FLOPS,
expr.span, expr.span,
"log base can be expressed more clearly", "log base can be expressed more clearly",
"consider using", "consider using",
format!("{}.log({})", Sugg::hir(cx, largs_self, "..").maybe_par(), Sugg::hir(cx, rargs_self, ".."),), format!(
"{}.log({})",
Sugg::hir(cx, largs_self, "..").maybe_par(),
Sugg::hir(cx, rargs_self, ".."),
),
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} }
}
} }
fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
if let ExprKind::Binary( if let ExprKind::Binary(
Spanned { Spanned {
node: BinOpKind::Div, .. node: BinOpKind::Div, ..
}, },
div_lhs, div_lhs,
div_rhs, div_rhs,
) = &expr.kind; ) = &expr.kind
if let ExprKind::Binary( && let ExprKind::Binary(
Spanned { Spanned {
node: BinOpKind::Mul, .. node: BinOpKind::Mul, ..
}, },
mul_lhs, mul_lhs,
mul_rhs, mul_rhs,
) = &div_lhs.kind; ) = &div_lhs.kind
if let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs); && let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs)
if let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs); && let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs)
then { {
// TODO: also check for constant values near PI/180 or 180/PI // TODO: also check for constant values near PI/180 or 180/PI
if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue) && if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue)
(F32(180_f32) == lvalue || F64(180_f64) == lvalue) && (F32(180_f32) == lvalue || F64(180_f64) == lvalue)
{ {
let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par()); let mut proposal = format!("{}.to_degrees()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
if_chain! { if let ExprKind::Lit(literal) = mul_lhs.kind
if let ExprKind::Lit(literal) = mul_lhs.kind; && let ast::LitKind::Float(ref value, float_type) = literal.node
if let ast::LitKind::Float(ref value, float_type) = literal.node; && float_type == ast::LitFloatType::Unsuffixed
if float_type == ast::LitFloatType::Unsuffixed; {
then {
if value.as_str().ends_with('.') { if value.as_str().ends_with('.') {
proposal = format!("{}0_f64.to_degrees()", Sugg::hir(cx, mul_lhs, "..")); proposal = format!("{}0_f64.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
} else { } else {
proposal = format!("{}_f64.to_degrees()", Sugg::hir(cx, mul_lhs, "..")); proposal = format!("{}_f64.to_degrees()", Sugg::hir(cx, mul_lhs, ".."));
} }
} }
}
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
SUBOPTIMAL_FLOPS, SUBOPTIMAL_FLOPS,
@ -684,23 +718,20 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
proposal, proposal,
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} else if } else if (F32(180_f32) == rvalue || F64(180_f64) == rvalue)
(F32(180_f32) == rvalue || F64(180_f64) == rvalue) && && (F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
(F32(f32_consts::PI) == lvalue || F64(f64_consts::PI) == lvalue)
{ {
let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par()); let mut proposal = format!("{}.to_radians()", Sugg::hir(cx, mul_lhs, "..").maybe_par());
if_chain! { if let ExprKind::Lit(literal) = mul_lhs.kind
if let ExprKind::Lit(literal) = mul_lhs.kind; && let ast::LitKind::Float(ref value, float_type) = literal.node
if let ast::LitKind::Float(ref value, float_type) = literal.node; && float_type == ast::LitFloatType::Unsuffixed
if float_type == ast::LitFloatType::Unsuffixed; {
then {
if value.as_str().ends_with('.') { if value.as_str().ends_with('.') {
proposal = format!("{}0_f64.to_radians()", Sugg::hir(cx, mul_lhs, "..")); proposal = format!("{}0_f64.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
} else { } else {
proposal = format!("{}_f64.to_radians()", Sugg::hir(cx, mul_lhs, "..")); proposal = format!("{}_f64.to_radians()", Sugg::hir(cx, mul_lhs, ".."));
} }
} }
}
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
SUBOPTIMAL_FLOPS, SUBOPTIMAL_FLOPS,
@ -712,7 +743,6 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
); );
} }
} }
}
} }
impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic { impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {

View file

@ -8,7 +8,6 @@ use clippy_utils::macros::{
}; };
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use clippy_utils::ty::{implements_trait, is_type_lang_item}; use clippy_utils::ty::{implements_trait, is_type_lang_item};
use if_chain::if_chain;
use itertools::Itertools; use itertools::Itertools;
use rustc_ast::{ use rustc_ast::{
FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions, FormatArgPosition, FormatArgPositionKind, FormatArgsPiece, FormatArgumentKind, FormatCount, FormatOptions,
@ -404,28 +403,25 @@ fn check_format_in_format_args(cx: &LateContext<'_>, call_site: Span, name: Symb
} }
fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) { fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Expr<'_>) {
if_chain! { if !value.span.from_expansion()
if !value.span.from_expansion(); && let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind
if let ExprKind::MethodCall(_, receiver, [], to_string_span) = value.kind; && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id)
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(value.hir_id); && is_diag_trait_item(cx, method_def_id, sym::ToString)
if is_diag_trait_item(cx, method_def_id, sym::ToString); && let receiver_ty = cx.typeck_results().expr_ty(receiver)
let receiver_ty = cx.typeck_results().expr_ty(receiver); && let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display)
if let Some(display_trait_id) = cx.tcx.get_diagnostic_item(sym::Display); && let (n_needed_derefs, target) =
let (n_needed_derefs, target) = count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter())
count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter()); && implements_trait(cx, target, display_trait_id, &[])
if implements_trait(cx, target, display_trait_id, &[]); && let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait()
if let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait(); && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
if let Some(receiver_snippet) = snippet_opt(cx, receiver.span); {
then {
let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]); let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
if n_needed_derefs == 0 && !needs_ref { if n_needed_derefs == 0 && !needs_ref {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
TO_STRING_IN_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS,
to_string_span.with_lo(receiver.span.hi()), to_string_span.with_lo(receiver.span.hi()),
&format!( &format!("`to_string` applied to a type that implements `Display` in `{name}!` args"),
"`to_string` applied to a type that implements `Display` in `{name}!` args"
),
"remove this", "remove this",
String::new(), String::new(),
Applicability::MachineApplicable, Applicability::MachineApplicable,
@ -435,9 +431,7 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
cx, cx,
TO_STRING_IN_FORMAT_ARGS, TO_STRING_IN_FORMAT_ARGS,
value.span, value.span,
&format!( &format!("`to_string` applied to a type that implements `Display` in `{name}!` args"),
"`to_string` applied to a type that implements `Display` in `{name}!` args"
),
"use this", "use this",
format!( format!(
"{}{:*>n_needed_derefs$}{receiver_snippet}", "{}{:*>n_needed_derefs$}{receiver_snippet}",
@ -448,7 +442,6 @@ fn check_to_string_in_format_args(cx: &LateContext<'_>, name: Symbol, value: &Ex
); );
} }
} }
}
} }
fn format_arg_positions( fn format_arg_positions(

View file

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
use clippy_utils::macros::{find_format_arg_expr, find_format_args, is_format_macro, root_macro_call_first_node}; use clippy_utils::macros::{find_format_arg_expr, find_format_args, is_format_macro, root_macro_call_first_node};
use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators}; use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
use if_chain::if_chain;
use rustc_ast::{FormatArgsPiece, FormatTrait}; use rustc_ast::{FormatArgsPiece, FormatTrait};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath}; use rustc_hir::{Expr, ExprKind, Impl, ImplItem, ImplItemKind, QPath};
@ -141,20 +140,19 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl {
} }
fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) { fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! { if let ExprKind::MethodCall(path, self_arg, ..) = expr.kind
// Get the hir_id of the object we are calling the method on // Get the hir_id of the object we are calling the method on
if let ExprKind::MethodCall(path, self_arg, ..) = expr.kind;
// Is the method to_string() ? // Is the method to_string() ?
if path.ident.name == sym::to_string; && path.ident.name == sym::to_string
// Is the method a part of the ToString trait? (i.e. not to_string() implemented // Is the method a part of the ToString trait? (i.e. not to_string() implemented
// separately) // separately)
if let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); && let Some(expr_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
if is_diag_trait_item(cx, expr_def_id, sym::ToString); && is_diag_trait_item(cx, expr_def_id, sym::ToString)
// Is the method is called on self // Is the method is called on self
if let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind; && let ExprKind::Path(QPath::Resolved(_, path)) = self_arg.kind
if let [segment] = path.segments; && let [segment] = path.segments
if segment.ident.name == kw::SelfLower; && segment.ident.name == kw::SelfLower
then { {
span_lint( span_lint(
cx, cx,
RECURSIVE_FORMAT_IMPL, RECURSIVE_FORMAT_IMPL,
@ -162,7 +160,6 @@ fn check_to_string_in_display(cx: &LateContext<'_>, expr: &Expr<'_>) {
"using `self.to_string` in `fmt::Display` implementation will cause infinite recursion", "using `self.to_string` in `fmt::Display` implementation will cause infinite recursion",
); );
} }
}
} }
fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTraitNames) { fn check_self_in_format_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, impl_trait: FormatTraitNames) {
@ -215,10 +212,9 @@ fn check_format_arg_self(cx: &LateContext<'_>, span: Span, arg: &Expr<'_>, impl_
} }
fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTraitNames) { fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait: FormatTraitNames) {
if_chain! { if let Some(macro_call) = root_macro_call_first_node(cx, expr)
if let Some(macro_call) = root_macro_call_first_node(cx, expr); && let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id)
if let Some(name) = cx.tcx.get_diagnostic_name(macro_call.def_id); {
then {
let replacement = match name { let replacement = match name {
sym::print_macro | sym::eprint_macro => "write", sym::print_macro | sym::eprint_macro => "write",
sym::println_macro | sym::eprintln_macro => "writeln", sym::println_macro | sym::eprintln_macro => "writeln",
@ -241,29 +237,28 @@ fn check_print_in_format_impl(cx: &LateContext<'_>, expr: &Expr<'_>, impl_trait:
Applicability::HasPlaceholders, Applicability::HasPlaceholders,
); );
} }
}
} }
fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Option<FormatTraitNames> { fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Option<FormatTraitNames> {
if_chain! { if impl_item.ident.name == sym::fmt
if impl_item.ident.name == sym::fmt; && let ImplItemKind::Fn(_, body_id) = impl_item.kind
if let ImplItemKind::Fn(_, body_id) = impl_item.kind; && let Some(Impl {
if let Some(Impl { of_trait: Some(trait_ref),..}) = get_parent_as_impl(cx.tcx, impl_item.hir_id()); of_trait: Some(trait_ref),
if let Some(did) = trait_ref.trait_def_id(); ..
if let Some(name) = cx.tcx.get_diagnostic_name(did); }) = get_parent_as_impl(cx.tcx, impl_item.hir_id())
if matches!(name, sym::Debug | sym::Display); && let Some(did) = trait_ref.trait_def_id()
then { && let Some(name) = cx.tcx.get_diagnostic_name(did)
&& matches!(name, sym::Debug | sym::Display)
{
let body = cx.tcx.hir().body(body_id); let body = cx.tcx.hir().body(body_id);
let formatter_name = body.params.get(1) let formatter_name = body
.params
.get(1)
.and_then(|param| param.pat.simple_ident()) .and_then(|param| param.pat.simple_ident())
.map(|ident| ident.name); .map(|ident| ident.name);
Some(FormatTraitNames { Some(FormatTraitNames { name, formatter_name })
name,
formatter_name,
})
} else { } else {
None None
} }
}
} }

View file

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note}; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
use clippy_utils::is_span_if; use clippy_utils::is_span_if;
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp}; use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
@ -168,21 +167,20 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
/// Implementation of the `SUSPICIOUS_UNARY_OP_FORMATTING` lint. /// Implementation of the `SUSPICIOUS_UNARY_OP_FORMATTING` lint.
fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) { fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) {
if_chain! { if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind
if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind; && !lhs.span.from_expansion() && !rhs.span.from_expansion()
if !lhs.span.from_expansion() && !rhs.span.from_expansion();
// span between BinOp LHS and RHS // span between BinOp LHS and RHS
let binop_span = lhs.span.between(rhs.span); && let binop_span = lhs.span.between(rhs.span)
// if RHS is an UnOp // if RHS is an UnOp
if let ExprKind::Unary(op, ref un_rhs) = rhs.kind; && let ExprKind::Unary(op, ref un_rhs) = rhs.kind
// from UnOp operator to UnOp operand // from UnOp operator to UnOp operand
let unop_operand_span = rhs.span.until(un_rhs.span); && let unop_operand_span = rhs.span.until(un_rhs.span)
if let Some(binop_snippet) = snippet_opt(cx, binop_span); && let Some(binop_snippet) = snippet_opt(cx, binop_span)
if let Some(unop_operand_snippet) = snippet_opt(cx, unop_operand_span); && let Some(unop_operand_snippet) = snippet_opt(cx, unop_operand_span)
let binop_str = BinOpKind::to_string(&binop.node); && let binop_str = BinOpKind::to_string(&binop.node)
// no space after BinOp operator and space after UnOp operator // no space after BinOp operator and space after UnOp operator
if binop_snippet.ends_with(binop_str) && unop_operand_snippet.ends_with(' '); && binop_snippet.ends_with(binop_str) && unop_operand_snippet.ends_with(' ')
then { {
let unop_str = UnOp::to_string(op); let unop_str = UnOp::to_string(op);
let eqop_span = lhs.span.between(un_rhs.span); let eqop_span = lhs.span.between(un_rhs.span);
span_lint_and_help( span_lint_and_help(
@ -194,52 +192,45 @@ fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) {
`{binop_str}{unop_str}` is a single operator" `{binop_str}{unop_str}` is a single operator"
), ),
None, None,
&format!( &format!("put a space between `{binop_str}` and `{unop_str}` and remove the space after `{unop_str}`"),
"put a space between `{binop_str}` and `{unop_str}` and remove the space after `{unop_str}`"
),
); );
} }
}
} }
/// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`. /// Implementation of the `SUSPICIOUS_ELSE_FORMATTING` lint for weird `else`.
fn check_else(cx: &EarlyContext<'_>, expr: &Expr) { fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
if_chain! { if let ExprKind::If(_, then, Some(else_)) = &expr.kind
if let ExprKind::If(_, then, Some(else_)) = &expr.kind; && (is_block(else_) || is_if(else_))
if is_block(else_) || is_if(else_); && !then.span.from_expansion() && !else_.span.from_expansion()
if !then.span.from_expansion() && !else_.span.from_expansion(); && !in_external_macro(cx.sess(), expr.span)
if !in_external_macro(cx.sess(), expr.span);
// workaround for rust-lang/rust#43081 // workaround for rust-lang/rust#43081
if expr.span.lo().0 != 0 && expr.span.hi().0 != 0; && expr.span.lo().0 != 0 && expr.span.hi().0 != 0
// this will be a span from the closing } of the “then” block (excluding) to // this will be a span from the closing } of the “then” block (excluding) to
// the “if” of the “else if” block (excluding) // the “if” of the “else if” block (excluding)
let else_span = then.span.between(else_.span); && let else_span = then.span.between(else_.span)
// the snippet should look like " else \n " with maybe comments anywhere // the snippet should look like " else \n " with maybe comments anywhere
// its bad when there is a \n after the “else” // its bad when there is a \n after the “else”
if let Some(else_snippet) = snippet_opt(cx, else_span); && let Some(else_snippet) = snippet_opt(cx, else_span)
if let Some((pre_else, post_else)) = else_snippet.split_once("else"); && let Some((pre_else, post_else)) = else_snippet.split_once("else")
if let Some((_, post_else_post_eol)) = post_else.split_once('\n'); && let Some((_, post_else_post_eol)) = post_else.split_once('\n')
{
then {
// Allow allman style braces `} \n else \n {` // Allow allman style braces `} \n else \n {`
if_chain! { if is_block(else_)
if is_block(else_); && let Some((_, pre_else_post_eol)) = pre_else.split_once('\n')
if let Some((_, pre_else_post_eol)) = pre_else.split_once('\n');
// Exactly one eol before and after the else // Exactly one eol before and after the else
if !pre_else_post_eol.contains('\n'); && !pre_else_post_eol.contains('\n')
if !post_else_post_eol.contains('\n'); && !post_else_post_eol.contains('\n')
then { {
return; return;
} }
}
// Don't warn if the only thing inside post_else_post_eol is a comment block. // Don't warn if the only thing inside post_else_post_eol is a comment block.
let trimmed_post_else_post_eol = post_else_post_eol.trim(); let trimmed_post_else_post_eol = post_else_post_eol.trim();
if trimmed_post_else_post_eol.starts_with("/*") && trimmed_post_else_post_eol.ends_with("*/") { if trimmed_post_else_post_eol.starts_with("/*") && trimmed_post_else_post_eol.ends_with("*/") {
return return;
} }
let else_desc = if is_if(else_) { "if" } else { "{..}" }; let else_desc = if is_if(else_) { "if" } else { "{..}" };
@ -255,7 +246,6 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
), ),
); );
} }
}
} }
#[must_use] #[must_use]
@ -272,15 +262,15 @@ fn indentation(cx: &EarlyContext<'_>, span: Span) -> usize {
fn check_array(cx: &EarlyContext<'_>, expr: &Expr) { fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
if let ExprKind::Array(ref array) = expr.kind { if let ExprKind::Array(ref array) = expr.kind {
for element in array { for element in array {
if_chain! { if let ExprKind::Binary(ref op, ref lhs, _) = element.kind
if let ExprKind::Binary(ref op, ref lhs, _) = element.kind; && has_unary_equivalent(op.node)
if has_unary_equivalent(op.node) && lhs.span.eq_ctxt(op.span); && lhs.span.eq_ctxt(op.span)
let space_span = lhs.span.between(op.span); && let space_span = lhs.span.between(op.span)
if let Some(space_snippet) = snippet_opt(cx, space_span); && let Some(space_snippet) = snippet_opt(cx, space_span)
let lint_span = lhs.span.with_lo(lhs.span.hi()); && let lint_span = lhs.span.with_lo(lhs.span.hi())
if space_snippet.contains('\n'); && space_snippet.contains('\n')
if indentation(cx, op.span) <= indentation(cx, lhs.span); && indentation(cx, op.span) <= indentation(cx, lhs.span)
then { {
span_lint_and_note( span_lint_and_note(
cx, cx,
POSSIBLE_MISSING_COMMA, POSSIBLE_MISSING_COMMA,
@ -292,24 +282,22 @@ fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
} }
} }
} }
}
} }
fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) { fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
if_chain! { if !first.span.from_expansion() && !second.span.from_expansion()
if !first.span.from_expansion() && !second.span.from_expansion(); && matches!(first.kind, ExprKind::If(..))
if matches!(first.kind, ExprKind::If(..)); && (is_block(second) || is_if(second))
if is_block(second) || is_if(second);
// Proc-macros can give weird spans. Make sure this is actually an `if`. // Proc-macros can give weird spans. Make sure this is actually an `if`.
if is_span_if(cx, first.span); && is_span_if(cx, first.span)
// If there is a line break between the two expressions, don't lint. // If there is a line break between the two expressions, don't lint.
// If there is a non-whitespace character, this span came from a proc-macro. // If there is a non-whitespace character, this span came from a proc-macro.
let else_span = first.span.between(second.span); && let else_span = first.span.between(second.span)
if let Some(else_snippet) = snippet_opt(cx, else_span); && let Some(else_snippet) = snippet_opt(cx, else_span)
if !else_snippet.chars().any(|c| c == '\n' || !c.is_whitespace()); && !else_snippet.chars().any(|c| c == '\n' || !c.is_whitespace())
then { {
let (looks_like, next_thing) = if is_if(second) { let (looks_like, next_thing) = if is_if(second) {
("an `else if`", "the second `if`") ("an `else if`", "the second `if`")
} else { } else {
@ -322,12 +310,9 @@ fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
else_span, else_span,
&format!("this looks like {looks_like} but the `else` is missing"), &format!("this looks like {looks_like} but the `else` is missing"),
None, None,
&format!( &format!("to remove this lint, add the missing `else` or add a new line before {next_thing}",),
"to remove this lint, add the missing `else` or add a new line before {next_thing}",
),
); );
} }
}
} }
fn is_block(expr: &Expr) -> bool { fn is_block(expr: &Expr) -> bool {

View file

@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::is_integer_literal; use clippy_utils::is_integer_literal;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item}; use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind}; use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -46,41 +45,31 @@ declare_lint_pass!(FromStrRadix10 => [FROM_STR_RADIX_10]);
impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 { impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) {
if_chain! { if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind
if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind; && let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind
if let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind;
// check if the first part of the path is some integer primitive // check if the first part of the path is some integer primitive
if let TyKind::Path(ty_qpath) = &ty.kind; && let TyKind::Path(ty_qpath) = &ty.kind
let ty_res = cx.qpath_res(ty_qpath, ty.hir_id); && let ty_res = cx.qpath_res(ty_qpath, ty.hir_id)
if let def::Res::PrimTy(prim_ty) = ty_res; && let def::Res::PrimTy(prim_ty) = ty_res
if matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_)); && matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_))
// check if the second part of the path indeed calls the associated // check if the second part of the path indeed calls the associated
// function `from_str_radix` // function `from_str_radix`
if pathseg.ident.name.as_str() == "from_str_radix"; && pathseg.ident.name.as_str() == "from_str_radix"
// check if the second argument is a primitive `10` // check if the second argument is a primitive `10`
if is_integer_literal(radix, 10); && is_integer_literal(radix, 10)
{
then {
let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind { let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind {
let ty = cx.typeck_results().expr_ty(expr); let ty = cx.typeck_results().expr_ty(expr);
if is_ty_stringish(cx, ty) { if is_ty_stringish(cx, ty) { expr } else { &src }
expr
} else {
&src
}
} else { } else {
&src &src
}; };
let sugg = Sugg::hir_with_applicability( let sugg =
cx, Sugg::hir_with_applicability(cx, expr, "<string>", &mut Applicability::MachineApplicable).maybe_par();
expr,
"<string>",
&mut Applicability::MachineApplicable
).maybe_par();
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
@ -89,11 +78,10 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
"this call to `from_str_radix` can be replaced with a call to `str::parse`", "this call to `from_str_radix` can be replaced with a call to `str::parse`",
"try", "try",
format!("{sugg}.parse::<{}>()", prim_ty.name_str()), format!("{sugg}.parse::<{}>()", prim_ty.name_str()),
Applicability::MaybeIncorrect Applicability::MaybeIncorrect,
); );
} }
} }
}
} }
/// Checks if a Ty is `String` or `&str` /// Checks if a Ty is `String` or `&str`

View file

@ -5,18 +5,10 @@ use rustc_hir as hir;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, GenericParam, Generics, HirId, ImplItem, ImplItemKind, TraitItem, TraitItemKind}; use rustc_hir::{Body, GenericParam, Generics, HirId, ImplItem, ImplItemKind, TraitItem, TraitItemKind};
use rustc_lint::LateContext; use rustc_lint::LateContext;
use rustc_span::symbol::Ident;
use rustc_span::{BytePos, Span};
use super::IMPL_TRAIT_IN_PARAMS; use super::IMPL_TRAIT_IN_PARAMS;
fn report( fn report(cx: &LateContext<'_>, param: &GenericParam<'_>, generics: &Generics<'_>) {
cx: &LateContext<'_>,
param: &GenericParam<'_>,
ident: &Ident,
generics: &Generics<'_>,
first_param_span: Span,
) {
// No generics with nested generics, and no generics like FnMut(x) // No generics with nested generics, and no generics like FnMut(x)
span_lint_and_then( span_lint_and_then(
cx, cx,
@ -35,12 +27,7 @@ fn report(
); );
} else { } else {
diag.span_suggestion_with_style( diag.span_suggestion_with_style(
Span::new( generics.span,
first_param_span.lo() - rustc_span::BytePos(1),
ident.span.hi(),
ident.span.ctxt(),
ident.span.parent(),
),
"add a type parameter", "add a type parameter",
format!("<{{ /* Generic name */ }}: {}>", &param.name.ident().as_str()[5..]), format!("<{{ /* Generic name */ }}: {}>", &param.name.ident().as_str()[5..]),
rustc_errors::Applicability::HasPlaceholders, rustc_errors::Applicability::HasPlaceholders,
@ -52,54 +39,47 @@ fn report(
} }
pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: &'tcx Body<'_>, hir_id: HirId) { pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: &'tcx Body<'_>, hir_id: HirId) {
if_chain! { if let FnKind::ItemFn(_, generics, _) = kind
if let FnKind::ItemFn(ident, generics, _) = kind; && cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public()
if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public(); && !is_in_test_function(cx.tcx, hir_id)
if !is_in_test_function(cx.tcx, hir_id); {
then {
for param in generics.params { for param in generics.params {
if param.is_impl_trait() { if param.is_impl_trait() {
report(cx, param, ident, generics, body.params[0].span); report(cx, param, generics);
}; };
} }
} }
}
} }
pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) { pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
if_chain! { if let ImplItemKind::Fn(_, body_id) = impl_item.kind
if let ImplItemKind::Fn(_, body_id) = impl_item.kind; && let hir::Node::Item(item) = cx.tcx.hir().get_parent(impl_item.hir_id())
if let hir::Node::Item(item) = cx.tcx.hir().get_parent(impl_item.hir_id()); && let hir::ItemKind::Impl(impl_) = item.kind
if let hir::ItemKind::Impl(impl_) = item.kind; && let hir::Impl { of_trait, .. } = *impl_
if let hir::Impl { of_trait, .. } = *impl_; && of_trait.is_none()
if of_trait.is_none(); && let body = cx.tcx.hir().body(body_id)
let body = cx.tcx.hir().body(body_id); && cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public()
if cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public(); && !is_in_test_function(cx.tcx, impl_item.hir_id())
if !is_in_test_function(cx.tcx, impl_item.hir_id()); {
then {
for param in impl_item.generics.params { for param in impl_item.generics.params {
if param.is_impl_trait() { if param.is_impl_trait() {
report(cx, param, &impl_item.ident, impl_item.generics, body.params[0].span); report(cx, param, impl_item.generics);
}
} }
} }
} }
} }
pub(super) fn check_trait_item(cx: &LateContext<'_>, trait_item: &TraitItem<'_>, avoid_breaking_exported_api: bool) { pub(super) fn check_trait_item(cx: &LateContext<'_>, trait_item: &TraitItem<'_>, avoid_breaking_exported_api: bool) {
if_chain! { if !avoid_breaking_exported_api
if !avoid_breaking_exported_api; && let TraitItemKind::Fn(_, _) = trait_item.kind
if let TraitItemKind::Fn(_, _) = trait_item.kind; && let hir::Node::Item(item) = cx.tcx.hir().get_parent(trait_item.hir_id())
if let hir::Node::Item(item) = cx.tcx.hir().get_parent(trait_item.hir_id());
// ^^ (Will always be a trait) // ^^ (Will always be a trait)
if !item.vis_span.is_empty(); // Is public && !item.vis_span.is_empty() // Is public
if !is_in_test_function(cx.tcx, trait_item.hir_id()); && !is_in_test_function(cx.tcx, trait_item.hir_id())
then { {
for param in trait_item.generics.params { for param in trait_item.generics.params {
if param.is_impl_trait() { if param.is_impl_trait() {
let sp = trait_item.ident.span.with_hi(trait_item.ident.span.hi() + BytePos(1)); report(cx, param, trait_item.generics);
report(cx, param, &trait_item.ident, trait_item.generics, sp.shrink_to_hi());
}
} }
} }
} }

View file

@ -43,15 +43,13 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
// Body must be &(mut) <self_data>.name // Body must be &(mut) <self_data>.name
// self_data is not necessarily self, to also lint sub-getters, etc… // self_data is not necessarily self, to also lint sub-getters, etc…
let block_expr = if_chain! { let block_expr = if let ExprKind::Block(block, _) = body.value.kind
if let ExprKind::Block(block,_) = body.value.kind; && block.stmts.is_empty()
if block.stmts.is_empty(); && let Some(block_expr) = block.expr
if let Some(block_expr) = block.expr; {
then {
block_expr block_expr
} else { } else {
return; return;
}
}; };
let expr_span = block_expr.span; let expr_span = block_expr.span;
@ -61,14 +59,12 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
} else { } else {
block_expr block_expr
}; };
let (self_data, used_ident) = if_chain! { let (self_data, used_ident) = if let ExprKind::Field(self_data, ident) = expr.kind
if let ExprKind::Field(self_data, ident) = expr.kind; && ident.name.as_str() != name
if ident.name.as_str() != name; {
then {
(self_data, ident) (self_data, ident)
} else { } else {
return; return;
}
}; };
let mut used_field = None; let mut used_field = None;

View file

@ -86,15 +86,15 @@ fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: S
} }
fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) { fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) {
if_chain! { if let Adt(adt, subst) = err_ty.kind()
if let Adt(adt, subst) = err_ty.kind(); && let Some(local_def_id) = err_ty
if let Some(local_def_id) = err_ty.ty_adt_def().expect("already checked this is adt").did().as_local(); .ty_adt_def()
if let Some(hir::Node::Item(item)) = cx .expect("already checked this is adt")
.tcx .did()
.hir() .as_local()
.find_by_def_id(local_def_id); && let Some(hir::Node::Item(item)) = cx.tcx.hir().find_by_def_id(local_def_id)
if let hir::ItemKind::Enum(ref def, _) = item.kind; && let hir::ItemKind::Enum(ref def, _) = item.kind
then { {
let variants_size = AdtVariantInfo::new(cx, *adt, subst); let variants_size = AdtVariantInfo::new(cx, *adt, subst);
if let Some((first_variant, variants)) = variants_size.split_first() if let Some((first_variant, variants)) = variants_size.split_first()
&& first_variant.size >= large_err_threshold && first_variant.size >= large_err_threshold
@ -115,17 +115,19 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty
let variant_def = &def.variants[variant.ind]; let variant_def = &def.variants[variant.ind];
diag.span_label( diag.span_label(
variant_def.span, variant_def.span,
format!("the variant `{}` contains at least {} bytes", variant_def.ident, variant.size), format!(
"the variant `{}` contains at least {} bytes",
variant_def.ident, variant.size
),
); );
} }
} }
diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`")); diag.help(format!("try reducing the size of `{err_ty}`, for example by boxing large elements or replacing it with `Box<{err_ty}>`"));
} },
); );
} }
} } else {
else {
let ty_size = approx_ty_size(cx, err_ty); let ty_size = approx_ty_size(cx, err_ty);
if ty_size >= large_err_threshold { if ty_size >= large_err_threshold {
span_lint_and_then( span_lint_and_then(
@ -140,5 +142,4 @@ fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty
); );
} }
} }
}
} }

View file

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{higher, SpanlessEq}; use clippy_utils::{higher, SpanlessEq};
use if_chain::if_chain;
use rustc_errors::Diagnostic; use rustc_errors::Diagnostic;
use rustc_hir::intravisit::{self as visit, Visitor}; use rustc_hir::intravisit::{self as visit, Visitor};
use rustc_hir::{Expr, ExprKind}; use rustc_hir::{Expr, ExprKind};
@ -127,15 +126,13 @@ impl<'tcx, 'l> ArmVisitor<'tcx, 'l> {
} }
fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
if_chain! { if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind
if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind; && path.ident.as_str() == "lock"
if path.ident.as_str() == "lock"; && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs()
let ty = cx.typeck_results().expr_ty(self_arg).peel_refs(); && is_type_diagnostic_item(cx, ty, sym::Mutex)
if is_type_diagnostic_item(cx, ty, sym::Mutex); {
then {
Some(self_arg) Some(self_arg)
} else { } else {
None None
} }
}
} }

View file

@ -10,10 +10,8 @@ use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{Ty, TypeckResults}; use rustc_middle::ty::{Ty, TypeckResults};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::Span;
use if_chain::if_chain;
use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::source::{snippet, snippet_opt}; use clippy_utils::source::{snippet, snippet_opt};
@ -337,20 +335,18 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't
} }
fn visit_expr(&mut self, e: &'tcx Expr<'_>) { fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
if_chain! { if let ExprKind::Call(fun, args) = e.kind
if let ExprKind::Call(fun, args) = e.kind; && let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind
if let ExprKind::Path(QPath::TypeRelative(ty, method)) = fun.kind; && let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind
if let TyKind::Path(QPath::Resolved(None, ty_path)) = ty.kind; && let Some(ty_did) = ty_path.res.opt_def_id()
if let Some(ty_did) = ty_path.res.opt_def_id(); {
then {
if self.target.ty() != self.maybe_typeck_results.unwrap().expr_ty(e) { if self.target.ty() != self.maybe_typeck_results.unwrap().expr_ty(e) {
return; return;
} }
if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) { if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) {
if method.ident.name == sym::new { if method.ident.name == sym::new {
self.suggestions self.suggestions.insert(e.span, "HashMap::default()".to_string());
.insert(e.span, "HashMap::default()".to_string());
} else if method.ident.name == sym!(with_capacity) { } else if method.ident.name == sym!(with_capacity) {
self.suggestions.insert( self.suggestions.insert(
e.span, e.span,
@ -362,8 +358,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't
} }
} else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) { } else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) {
if method.ident.name == sym::new { if method.ident.name == sym::new {
self.suggestions self.suggestions.insert(e.span, "HashSet::default()".to_string());
.insert(e.span, "HashSet::default()".to_string());
} else if method.ident.name == sym!(with_capacity) { } else if method.ident.name == sym!(with_capacity) {
self.suggestions.insert( self.suggestions.insert(
e.span, e.span,
@ -375,7 +370,6 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'a, 'b, 't
} }
} }
} }
}
walk_expr(self, e); walk_expr(self, e);
} }

View file

@ -2,7 +2,6 @@ use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::get_parent_expr; use clippy_utils::get_parent_expr;
use clippy_utils::source::snippet_with_context; use clippy_utils::source::snippet_with_context;
use if_chain::if_chain;
use rustc_ast::ast::{LitIntType, LitKind}; use rustc_ast::ast::{LitIntType, LitKind};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind};
@ -40,30 +39,39 @@ declare_lint_pass!(ImplicitSaturatingAdd => [IMPLICIT_SATURATING_ADD]);
impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd { impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
if_chain! { if let ExprKind::If(cond, then, None) = expr.kind
if let ExprKind::If(cond, then, None) = expr.kind; && let ExprKind::DropTemps(expr1) = cond.kind
if let ExprKind::DropTemps(expr1) = cond.kind; && let Some((c, op_node, l)) = get_const(cx, expr1)
if let Some((c, op_node, l)) = get_const(cx, expr1); && let BinOpKind::Ne | BinOpKind::Lt = op_node
if let BinOpKind::Ne | BinOpKind::Lt = op_node; && let ExprKind::Block(block, None) = then.kind
if let ExprKind::Block(block, None) = then.kind; && let Block {
if let Block {
stmts: stmts:
[Stmt [
{ kind: StmtKind::Expr(ex) | StmtKind::Semi(ex), .. }], Stmt {
expr: None, ..} | kind: StmtKind::Expr(ex) | StmtKind::Semi(ex),
Block { stmts: [], expr: Some(ex), ..} = block; ..
if let ExprKind::AssignOp(op1, target, value) = ex.kind; },
let ty = cx.typeck_results().expr_ty(target); ],
if Some(c) == get_int_max(ty); expr: None,
let ctxt = expr.span.ctxt(); ..
if ex.span.ctxt() == ctxt; }
if expr1.span.ctxt() == ctxt; | Block {
if clippy_utils::SpanlessEq::new(cx).eq_expr(l, target); stmts: [],
if BinOpKind::Add == op1.node; expr: Some(ex),
if let ExprKind::Lit(lit) = value.kind; ..
if let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node; } = block
if block.expr.is_none(); && let ExprKind::AssignOp(op1, target, value) = ex.kind
then { && let ty = cx.typeck_results().expr_ty(target)
&& Some(c) == get_int_max(ty)
&& let ctxt = expr.span.ctxt()
&& ex.span.ctxt() == ctxt
&& expr1.span.ctxt() == ctxt
&& clippy_utils::SpanlessEq::new(cx).eq_expr(l, target)
&& BinOpKind::Add == op1.node
&& let ExprKind::Lit(lit) = value.kind
&& let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node
&& block.expr.is_none()
{
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let code = snippet_with_context(cx, target.span, ctxt, "_", &mut app).0; let code = snippet_with_context(cx, target.span, ctxt, "_", &mut app).0;
let sugg = if let Some(parent) = get_parent_expr(cx, expr) let sugg = if let Some(parent) = get_parent_expr(cx, expr)
@ -74,8 +82,15 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
} else { } else {
format!("{code} = {code}.saturating_add(1);") format!("{code} = {code}.saturating_add(1);")
}; };
span_lint_and_sugg(cx, IMPLICIT_SATURATING_ADD, expr.span, "manual saturating add detected", "use instead", sugg, app); span_lint_and_sugg(
} cx,
IMPLICIT_SATURATING_ADD,
expr.span,
"manual saturating add detected",
"use instead",
sugg,
app,
);
} }
} }
} }

View file

@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::{higher, is_integer_literal, peel_blocks_with_stmt, SpanlessEq}; use clippy_utils::{higher, is_integer_literal, peel_blocks_with_stmt, SpanlessEq};
use if_chain::if_chain;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, QPath}; use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
@ -46,22 +45,20 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
if expr.span.from_expansion() { if expr.span.from_expansion() {
return; return;
} }
if_chain! { if let Some(higher::If { cond, then, r#else: None }) = higher::If::hir(expr)
if let Some(higher::If { cond, then, r#else: None }) = higher::If::hir(expr);
// Check if the conditional expression is a binary operation // Check if the conditional expression is a binary operation
if let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind; && let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind
// Ensure that the binary operator is >, !=, or < // Ensure that the binary operator is >, !=, or <
if BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node; && (BinOpKind::Ne == cond_op.node || BinOpKind::Gt == cond_op.node || BinOpKind::Lt == cond_op.node)
// Check if assign operation is done // Check if assign operation is done
if let Some(target) = subtracts_one(cx, then); && let Some(target) = subtracts_one(cx, then)
// Extracting out the variable name // Extracting out the variable name
if let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind; && let ExprKind::Path(QPath::Resolved(_, ares_path)) = target.kind
{
then {
// Handle symmetric conditions in the if statement // Handle symmetric conditions in the if statement
let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) { let (cond_var, cond_num_val) = if SpanlessEq::new(cx).eq_expr(cond_left, target) {
if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node { if BinOpKind::Gt == cond_op.node || BinOpKind::Ne == cond_op.node {
@ -97,35 +94,30 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
} }
}, },
ExprKind::Path(QPath::TypeRelative(_, name)) => { ExprKind::Path(QPath::TypeRelative(_, name)) => {
if_chain! { if name.ident.as_str() == "MIN"
if name.ident.as_str() == "MIN"; && let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id)
if let Some(const_id) = cx.typeck_results().type_dependent_def_id(cond_num_val.hir_id); && let Some(impl_id) = cx.tcx.impl_of_method(const_id)
if let Some(impl_id) = cx.tcx.impl_of_method(const_id); && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl && cx.tcx.type_of(impl_id).instantiate_identity().is_integral()
if cx.tcx.type_of(impl_id).instantiate_identity().is_integral(); {
then { print_lint_and_sugg(cx, var_name, expr);
print_lint_and_sugg(cx, var_name, expr)
}
} }
}, },
ExprKind::Call(func, []) => { ExprKind::Call(func, []) => {
if_chain! { if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind
if let ExprKind::Path(QPath::TypeRelative(_, name)) = func.kind; && name.ident.as_str() == "min_value"
if name.ident.as_str() == "min_value"; && let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id)
if let Some(func_id) = cx.typeck_results().type_dependent_def_id(func.hir_id); && let Some(impl_id) = cx.tcx.impl_of_method(func_id)
if let Some(impl_id) = cx.tcx.impl_of_method(func_id); && let None = cx.tcx.impl_trait_ref(impl_id) // An inherent impl
if let None = cx.tcx.impl_trait_ref(impl_id); // An inherent impl && cx.tcx.type_of(impl_id).instantiate_identity().is_integral()
if cx.tcx.type_of(impl_id).instantiate_identity().is_integral(); {
then { print_lint_and_sugg(cx, var_name, expr);
print_lint_and_sugg(cx, var_name, expr)
}
} }
}, },
_ => (), _ => (),
} }
} }
} }
}
} }
fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> { fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
@ -135,19 +127,15 @@ fn subtracts_one<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a Exp
(BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target) (BinOpKind::Sub == op1.node && is_integer_literal(value, 1)).then_some(target)
}, },
ExprKind::Assign(target, value, _) => { ExprKind::Assign(target, value, _) => {
if_chain! { if let ExprKind::Binary(ref op1, left1, right1) = value.kind
if let ExprKind::Binary(ref op1, left1, right1) = value.kind; && BinOpKind::Sub == op1.node
if BinOpKind::Sub == op1.node; && SpanlessEq::new(cx).eq_expr(left1, target)
&& is_integer_literal(right1, 1)
if SpanlessEq::new(cx).eq_expr(left1, target); {
if is_integer_literal(right1, 1);
then {
Some(target) Some(target)
} else { } else {
None None
} }
}
}, },
_ => None, _ => None,
} }

View file

@ -43,7 +43,7 @@ declare_clippy_lint! {
/// Box::new(123) /// Box::new(123)
/// } /// }
/// ``` /// ```
#[clippy::version = "1.73.0"] #[clippy::version = "1.74.0"]
pub IMPLIED_BOUNDS_IN_IMPLS, pub IMPLIED_BOUNDS_IN_IMPLS,
nursery, nursery,
"specifying bounds that are implied by other bounds in `impl Trait` type" "specifying bounds that are implied by other bounds in `impl Trait` type"

View file

@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{self as hir, ExprKind}; use rustc_hir::{self as hir, ExprKind};
@ -66,15 +65,14 @@ declare_lint_pass!(InconsistentStructConstructor => [INCONSISTENT_STRUCT_CONSTRU
impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor { impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if_chain! { if !expr.span.from_expansion()
if !expr.span.from_expansion(); && let ExprKind::Struct(qpath, fields, base) = expr.kind
if let ExprKind::Struct(qpath, fields, base) = expr.kind; && let ty = cx.typeck_results().expr_ty(expr)
let ty = cx.typeck_results().expr_ty(expr); && let Some(adt_def) = ty.ty_adt_def()
if let Some(adt_def) = ty.ty_adt_def(); && adt_def.is_struct()
if adt_def.is_struct(); && let Some(variant) = adt_def.variants().iter().next()
if let Some(variant) = adt_def.variants().iter().next(); && fields.iter().all(|f| f.is_shorthand)
if fields.iter().all(|f| f.is_shorthand); {
then {
let mut def_order_map = FxHashMap::default(); let mut def_order_map = FxHashMap::default();
for (idx, field) in variant.fields.iter().enumerate() { for (idx, field) in variant.fields.iter().enumerate() {
def_order_map.insert(field.name, idx); def_order_map.insert(field.name, idx);
@ -100,7 +98,8 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
String::new() String::new()
}; };
let sugg = format!("{} {{ {fields_snippet}{base_snippet} }}", let sugg = format!(
"{} {{ {fields_snippet}{base_snippet} }}",
snippet(cx, qpath.span(), ".."), snippet(cx, qpath.span(), ".."),
); );
@ -112,8 +111,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
"try", "try",
sugg, sugg,
Applicability::MachineApplicable, Applicability::MachineApplicable,
) );
}
} }
} }
} }

View file

@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::IfLet; use clippy_utils::higher::IfLet;
use clippy_utils::ty::is_copy; use clippy_utils::ty::is_copy;
use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local}; use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local};
use if_chain::if_chain;
use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
@ -70,23 +69,20 @@ impl_lint_pass!(IndexRefutableSlice => [INDEX_REFUTABLE_SLICE]);
impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice { impl<'tcx> LateLintPass<'tcx> for IndexRefutableSlice {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if_chain! { if (!expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some())
if !expr.span.from_expansion() || is_expn_of(expr.span, "if_chain").is_some(); && let Some(IfLet { let_pat, if_then, .. }) = IfLet::hir(cx, expr)
if let Some(IfLet {let_pat, if_then, ..}) = IfLet::hir(cx, expr); && !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id)
if !is_lint_allowed(cx, INDEX_REFUTABLE_SLICE, expr.hir_id); && self.msrv.meets(msrvs::SLICE_PATTERNS)
if self.msrv.meets(msrvs::SLICE_PATTERNS); && let found_slices = find_slice_values(cx, let_pat)
&& !found_slices.is_empty()
let found_slices = find_slice_values(cx, let_pat); && let filtered_slices = filter_lintable_slices(cx, found_slices, self.max_suggested_slice, if_then)
if !found_slices.is_empty(); && !filtered_slices.is_empty()
let filtered_slices = filter_lintable_slices(cx, found_slices, self.max_suggested_slice, if_then); {
if !filtered_slices.is_empty();
then {
for slice in filtered_slices.values() { for slice in filtered_slices.values() {
lint_slice(cx, slice); lint_slice(cx, slice);
} }
} }
} }
}
extract_msrv_attr!(LateContext); extract_msrv_attr!(LateContext);
} }
@ -245,29 +241,27 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> {
max_suggested_slice, max_suggested_slice,
} = *self; } = *self;
if_chain! { if let Some(use_info) = slice_lint_info.get_mut(&local_id)
// Check if this is even a local we're interested in // Check if this is even a local we're interested in
if let Some(use_info) = slice_lint_info.get_mut(&local_id);
let map = cx.tcx.hir(); && let map = cx.tcx.hir()
// Checking for slice indexing // Checking for slice indexing
let parent_id = map.parent_id(expr.hir_id); && let parent_id = map.parent_id(expr.hir_id)
if let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id); && let Some(hir::Node::Expr(parent_expr)) = map.find(parent_id)
if let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind; && let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind
if let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr); && let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr)
if let Ok(index_value) = index_value.try_into(); && let Ok(index_value) = index_value.try_into()
if index_value < max_suggested_slice; && index_value < max_suggested_slice
// Make sure that this slice index is read only // Make sure that this slice index is read only
let maybe_addrof_id = map.parent_id(parent_id); && let maybe_addrof_id = map.parent_id(parent_id)
if let Some(hir::Node::Expr(maybe_addrof_expr)) = map.find(maybe_addrof_id); && let Some(hir::Node::Expr(maybe_addrof_expr)) = map.find(maybe_addrof_id)
if let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind; && let hir::ExprKind::AddrOf(_kind, hir::Mutability::Not, _inner_expr) = maybe_addrof_expr.kind
then { {
use_info.index_use.push((index_value, map.span(parent_expr.hir_id))); use_info.index_use.push((index_value, map.span(parent_expr.hir_id)));
return; return;
} }
}
// The slice was used for something other than indexing // The slice was used for something other than indexing
self.slice_lint_info.remove(&local_id); self.slice_lint_info.remove(&local_id);

View file

@ -89,27 +89,17 @@ impl LateLintPass<'_> for InstantSubtraction {
rhs, rhs,
) = expr.kind ) = expr.kind
{ {
if_chain! { if is_instant_now_call(cx, lhs)
if is_instant_now_call(cx, lhs); && is_an_instant(cx, rhs)
&& let Some(sugg) = Sugg::hir_opt(cx, rhs)
if is_an_instant(cx, rhs); {
if let Some(sugg) = Sugg::hir_opt(cx, rhs); print_manual_instant_elapsed_sugg(cx, expr, sugg);
} else if !expr.span.from_expansion()
then { && self.msrv.meets(msrvs::TRY_FROM)
print_manual_instant_elapsed_sugg(cx, expr, sugg) && is_an_instant(cx, lhs)
} else { && is_a_duration(cx, rhs)
if_chain! { {
if !expr.span.from_expansion(); print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr);
if self.msrv.meets(msrvs::TRY_FROM);
if is_an_instant(cx, lhs);
if is_a_duration(cx, rhs);
then {
print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr)
}
}
}
} }
} }
} }

View file

@ -7,8 +7,8 @@ use clippy_utils::str_utils::{camel_case_split, count_match_end, count_match_sta
use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData}; use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, Variant, VariantData};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::Span;
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
use rustc_span::Span;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does

View file

@ -0,0 +1,78 @@
use clippy_utils::diagnostics::span_lint;
use clippy_utils::higher::ForLoop;
use clippy_utils::match_any_def_paths;
use clippy_utils::paths::{
HASHMAP_DRAIN, HASHMAP_ITER, HASHMAP_ITER_MUT, HASHMAP_KEYS, HASHMAP_VALUES, HASHMAP_VALUES_MUT, HASHSET_DRAIN,
HASHSET_ITER_TY,
};
use clippy_utils::ty::is_type_diagnostic_item;
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
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
/// 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.
///
/// ### Example
/// ```no_run
/// let my_map = std::collections::HashMap::<i32, String>::new();
/// for (key, value) in my_map { /* ... */ }
/// ```
/// Use instead:
/// ```no_run
/// let my_map = std::collections::HashMap::<i32, String>::new();
/// let mut keys = my_map.keys().clone().collect::<Vec<_>>();
/// keys.sort();
/// for key in keys {
/// let value = &my_map[key];
/// }
/// ```
#[clippy::version = "1.75.0"]
pub ITER_OVER_HASH_TYPE,
restriction,
"iterating over unordered hash-based types (`HashMap` and `HashSet`)"
}
declare_lint_pass!(IterOverHashType => [ITER_OVER_HASH_TYPE]);
impl LateLintPass<'_> for IterOverHashType {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ rustc_hir::Expr<'_>) {
if let Some(for_loop) = ForLoop::hir(expr)
&& !for_loop.body.span.from_expansion()
&& let ty = cx.typeck_results().expr_ty(for_loop.arg).peel_refs()
&& let Some(adt) = ty.ty_adt_def()
&& let did = adt.did()
&& (match_any_def_paths(
cx,
did,
&[
&HASHMAP_KEYS,
&HASHMAP_VALUES,
&HASHMAP_VALUES_MUT,
&HASHMAP_ITER,
&HASHMAP_ITER_MUT,
&HASHMAP_DRAIN,
&HASHSET_ITER_TY,
&HASHSET_DRAIN,
],
)
.is_some()
|| is_type_diagnostic_item(cx, ty, sym::HashMap)
|| is_type_diagnostic_item(cx, ty, sym::HashSet))
{
span_lint(
cx,
ITER_OVER_HASH_TYPE,
expr.span,
"iteration over unordered hash-based type",
);
};
}
}

View file

@ -1,5 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Item, ItemKind}; use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -47,21 +46,19 @@ impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]);
impl<'tcx> LateLintPass<'tcx> for LargeConstArrays { impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
if_chain! { if !item.span.from_expansion()
if !item.span.from_expansion(); && let ItemKind::Const(_, generics, _) = &item.kind
if let ItemKind::Const(_, generics, _) = &item.kind;
// Since static items may not have generics, skip generic const items. // Since static items may not have generics, skip generic const items.
// FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it // FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it
// doesn't account for empty where-clauses that only consist of keyword `where` IINM. // doesn't account for empty where-clauses that only consist of keyword `where` IINM.
if generics.params.is_empty() && !generics.has_where_clause_predicates; && generics.params.is_empty() && !generics.has_where_clause_predicates
let ty = cx.tcx.type_of(item.owner_id).instantiate_identity(); && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
if let ty::Array(element_type, cst) = ty.kind(); && let ty::Array(element_type, cst) = ty.kind()
if let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind(); && let ConstKind::Value(ty::ValTree::Leaf(element_count)) = cst.kind()
if let Ok(element_count) = element_count.try_to_target_usize(cx.tcx); && let Ok(element_count) = element_count.try_to_target_usize(cx.tcx)
if let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes()); && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
if self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size); && self.maximum_allowed_size < u128::from(element_count) * u128::from(element_size)
{
then {
let hi_pos = item.ident.span.lo() - BytePos::from_usize(1); let hi_pos = item.ident.span.lo() - BytePos::from_usize(1);
let sugg_span = Span::new( let sugg_span = Span::new(
hi_pos - BytePos::from_usize("const".len()), hi_pos - BytePos::from_usize("const".len()),
@ -81,9 +78,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
"static", "static",
Applicability::MachineApplicable, Applicability::MachineApplicable,
); );
} },
); );
} }
} }
}
} }

View file

@ -50,13 +50,12 @@ impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);
impl LateLintPass<'_> for LargeIncludeFile { impl LateLintPass<'_> for LargeIncludeFile {
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
if_chain! { if let Some(macro_call) = root_macro_call_first_node(cx, expr)
if let Some(macro_call) = root_macro_call_first_node(cx, expr); && !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id)
if !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id); && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
if cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id) || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id))
|| cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id); && let ExprKind::Lit(lit) = &expr.kind
if let ExprKind::Lit(lit) = &expr.kind; {
then {
let len = match &lit.node { let len = match &lit.node {
// include_bytes // include_bytes
LitKind::ByteStr(bstr, _) => bstr.len(), LitKind::ByteStr(bstr, _) => bstr.len(),
@ -82,5 +81,4 @@ impl LateLintPass<'_> for LargeIncludeFile {
); );
} }
} }
}
} }

View file

@ -2,7 +2,6 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_the
use clippy_utils::source::snippet_with_context; use clippy_utils::source::snippet_with_context;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators}; use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators};
use if_chain::if_chain;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::Res; use rustc_hir::def::Res;
@ -15,9 +14,9 @@ use rustc_hir::{
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, AssocKind, FnSig, Ty}; use rustc_middle::ty::{self, AssocKind, FnSig, Ty};
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{Span, Symbol};
use rustc_span::source_map::Spanned; use rustc_span::source_map::Spanned;
use rustc_span::symbol::sym; use rustc_span::symbol::sym;
use rustc_span::{Span, Symbol};
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -132,25 +131,22 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
} }
fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
if_chain! { if item.ident.name == sym::len
if item.ident.name == sym::len; && let ImplItemKind::Fn(sig, _) = &item.kind
if let ImplItemKind::Fn(sig, _) = &item.kind; && sig.decl.implicit_self.has_implicit_self()
if sig.decl.implicit_self.has_implicit_self(); && sig.decl.inputs.len() == 1
if sig.decl.inputs.len() == 1; && cx.effective_visibilities.is_exported(item.owner_id.def_id)
if cx.effective_visibilities.is_exported(item.owner_id.def_id); && matches!(sig.decl.output, FnRetTy::Return(_))
if matches!(sig.decl.output, FnRetTy::Return(_)); && let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id())
if let Some(imp) = get_parent_as_impl(cx.tcx, item.hir_id()); && imp.of_trait.is_none()
if imp.of_trait.is_none(); && let TyKind::Path(ty_path) = &imp.self_ty.kind
if let TyKind::Path(ty_path) = &imp.self_ty.kind; && let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id()
if let Some(ty_id) = cx.qpath_res(ty_path, imp.self_ty.hir_id).opt_def_id(); && let Some(local_id) = ty_id.as_local()
if let Some(local_id) = ty_id.as_local(); && let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id)
let ty_hir_id = cx.tcx.hir().local_def_id_to_hir_id(local_id); && !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id)
if !is_lint_allowed(cx, LEN_WITHOUT_IS_EMPTY, ty_hir_id); && let Some(output) =
if let Some(output) = parse_len_output( parse_len_output(cx, cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder())
cx, {
cx.tcx.fn_sig(item.owner_id).instantiate_identity().skip_binder()
);
then {
let (name, kind) = match cx.tcx.hir().find(ty_hir_id) { let (name, kind) = match cx.tcx.hir().find(ty_hir_id) {
Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"), Some(Node::ForeignItem(x)) => (x.ident.name, "extern type"),
Some(Node::Item(x)) => match x.kind { Some(Node::Item(x)) => match x.kind {
@ -158,11 +154,10 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
ItemKind::Enum(..) => (x.ident.name, "enum"), ItemKind::Enum(..) => (x.ident.name, "enum"),
ItemKind::Union(..) => (x.ident.name, "union"), ItemKind::Union(..) => (x.ident.name, "union"),
_ => (x.ident.name, "type"), _ => (x.ident.name, "type"),
} },
_ => return, _ => return,
}; };
check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind) check_for_is_empty(cx, sig.span, sig.decl.implicit_self, output, ty_id, name, kind);
}
} }
} }

View file

@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::path_to_local_id; use clippy_utils::path_to_local_id;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::visitors::is_local_used; use clippy_utils::visitors::is_local_used;
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::{BindingAnnotation, Mutability}; use rustc_hir::{BindingAnnotation, Mutability};
@ -61,24 +60,32 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) { fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
let mut it = block.stmts.iter().peekable(); let mut it = block.stmts.iter().peekable();
while let Some(stmt) = it.next() { while let Some(stmt) = it.next() {
if_chain! { if let Some(expr) = it.peek()
if let Some(expr) = it.peek(); && let hir::StmtKind::Local(local) = stmt.kind
if let hir::StmtKind::Local(local) = stmt.kind; && let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind
if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind; && let hir::StmtKind::Expr(if_) = expr.kind
if let hir::StmtKind::Expr(if_) = expr.kind; && let hir::ExprKind::If(
if let hir::ExprKind::If(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind; hir::Expr {
if !is_local_used(cx, *cond, canonical_id); kind: hir::ExprKind::DropTemps(cond),
if let hir::ExprKind::Block(then, _) = then.kind; ..
if let Some(value) = check_assign(cx, canonical_id, then); },
if !is_local_used(cx, value, canonical_id); then,
then { else_,
) = if_.kind
&& !is_local_used(cx, *cond, canonical_id)
&& let hir::ExprKind::Block(then, _) = then.kind
&& let Some(value) = check_assign(cx, canonical_id, then)
&& !is_local_used(cx, value, canonical_id)
{
let span = stmt.span.to(if_.span); let span = stmt.span.to(if_.span);
let has_interior_mutability = !cx.typeck_results().node_type(canonical_id).is_freeze( let has_interior_mutability = !cx
cx.tcx, .typeck_results()
cx.param_env, .node_type(canonical_id)
); .is_freeze(cx.tcx, cx.param_env);
if has_interior_mutability { return; } if has_interior_mutability {
return;
}
let (default_multi_stmts, default) = if let Some(else_) = else_ { let (default_multi_stmts, default) = if let Some(else_) = else_ {
if let hir::ExprKind::Block(else_, _) = else_.kind { if let hir::ExprKind::Block(else_, _) = else_.kind {
@ -115,7 +122,8 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
value=snippet(cx, value.span, "<value>"), value=snippet(cx, value.span, "<value>"),
default=snippet(cx, default.span, "<default>"), default=snippet(cx, default.span, "<default>"),
); );
span_lint_and_then(cx, span_lint_and_then(
cx,
USELESS_LET_IF_SEQ, USELESS_LET_IF_SEQ,
span, span,
"`if _ { .. } else { .. }` is an expression", "`if _ { .. } else { .. }` is an expression",
@ -129,8 +137,8 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
if !mutability.is_empty() { if !mutability.is_empty() {
diag.note("you might not need `mut` at all"); diag.note("you might not need `mut` at all");
} }
}); },
} );
} }
} }
} }
@ -141,14 +149,18 @@ fn check_assign<'tcx>(
decl: hir::HirId, decl: hir::HirId,
block: &'tcx hir::Block<'_>, block: &'tcx hir::Block<'_>,
) -> Option<&'tcx hir::Expr<'tcx>> { ) -> Option<&'tcx hir::Expr<'tcx>> {
if_chain! { if block.expr.is_none()
if block.expr.is_none(); && let Some(expr) = block.stmts.iter().last()
if let Some(expr) = block.stmts.iter().last(); && let hir::StmtKind::Semi(expr) = expr.kind
if let hir::StmtKind::Semi(expr) = expr.kind; && let hir::ExprKind::Assign(var, value, _) = expr.kind
if let hir::ExprKind::Assign(var, value, _) = expr.kind; && path_to_local_id(var, decl)
if path_to_local_id(var, decl); {
then { if block
if block.stmts.iter().take(block.stmts.len()-1).any(|stmt| is_local_used(cx, stmt, decl)) { .stmts
.iter()
.take(block.stmts.len() - 1)
.any(|stmt| is_local_used(cx, stmt, decl))
{
None None
} else { } else {
Some(value) Some(value)
@ -156,5 +168,4 @@ fn check_assign<'tcx>(
} else { } else {
None None
} }
}
} }

View file

@ -27,12 +27,11 @@ declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]);
impl LateLintPass<'_> for UnderscoreTyped { impl LateLintPass<'_> for UnderscoreTyped {
fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) { fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
if_chain! { if !in_external_macro(cx.tcx.sess, local.span)
if !in_external_macro(cx.tcx.sess, local.span); && let Some(ty) = local.ty // Ensure that it has a type defined
if let Some(ty) = local.ty; // Ensure that it has a type defined && let TyKind::Infer = &ty.kind // that type is '_'
if let TyKind::Infer = &ty.kind; // that type is '_' && local.span.eq_ctxt(ty.span)
if local.span.eq_ctxt(ty.span); {
then {
// NOTE: Using `is_from_proc_macro` on `init` will require that it's initialized, // NOTE: Using `is_from_proc_macro` on `init` will require that it's initialized,
// this doesn't. Alternatively, `WithSearchPat` can be implemented for `Ty` // this doesn't. Alternatively, `WithSearchPat` can be implemented for `Ty`
if snippet(cx, ty.span, "_").trim() != "_" { if snippet(cx, ty.span, "_").trim() != "_" {
@ -45,9 +44,8 @@ impl LateLintPass<'_> for UnderscoreTyped {
local.span, local.span,
"variable declared with type underscore", "variable declared with type underscore",
Some(ty.span.with_lo(local.pat.span.hi())), Some(ty.span.with_lo(local.pat.span.hi())),
"remove the explicit type `_` declaration" "remove the explicit type `_` declaration",
) );
}
}; };
} }
} }

View file

@ -52,7 +52,6 @@ extern crate declare_clippy_lint;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_lint::{Lint, LintId}; use rustc_lint::{Lint, LintId};
use rustc_session::Session;
#[cfg(feature = "internal")] #[cfg(feature = "internal")]
pub mod deprecated_lints; pub mod deprecated_lints;
@ -165,6 +164,7 @@ mod item_name_repetitions;
mod items_after_statements; mod items_after_statements;
mod items_after_test_module; mod items_after_test_module;
mod iter_not_returning_iterator; mod iter_not_returning_iterator;
mod iter_over_hash_type;
mod iter_without_into_iter; mod iter_without_into_iter;
mod large_const_arrays; mod large_const_arrays;
mod large_enum_variant; mod large_enum_variant;
@ -308,7 +308,6 @@ mod slow_vector_initialization;
mod std_instead_of_core; mod std_instead_of_core;
mod strings; mod strings;
mod strlen_on_c_strings; mod strlen_on_c_strings;
mod suspicious_doc_comments;
mod suspicious_operation_groupings; mod suspicious_operation_groupings;
mod suspicious_trait_impl; mod suspicious_trait_impl;
mod suspicious_xor_used_as_pow; mod suspicious_xor_used_as_pow;
@ -492,11 +491,83 @@ fn register_categories(store: &mut rustc_lint::LintStore) {
groups.register(store); groups.register(store);
} }
/// Register all lints and lint groups with the rustc plugin registry /// Register all lints and lint groups with the rustc lint store
/// ///
/// Used in `./src/driver.rs`. /// Used in `./src/driver.rs`.
#[expect(clippy::too_many_lines)] #[expect(clippy::too_many_lines)]
pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &'static Conf) { pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
let Conf {
ref absolute_paths_allowed_crates,
absolute_paths_max_segments,
accept_comment_above_attributes,
accept_comment_above_statement,
allow_dbg_in_tests,
allow_expect_in_tests,
allow_mixed_uninlined_format_args,
allow_one_hash_in_raw_strings,
allow_print_in_tests,
allow_private_module_inception,
allow_unwrap_in_tests,
ref allowed_dotfiles,
ref allowed_idents_below_min_chars,
ref allowed_scripts,
ref arithmetic_side_effects_allowed_binary,
ref arithmetic_side_effects_allowed_unary,
ref arithmetic_side_effects_allowed,
array_size_threshold,
avoid_breaking_exported_api,
ref await_holding_invalid_types,
cargo_ignore_publish,
cognitive_complexity_threshold,
ref disallowed_macros,
ref disallowed_methods,
ref disallowed_names,
ref disallowed_types,
ref doc_valid_idents,
enable_raw_pointer_heuristic_for_send,
enforce_iter_loop_reborrow,
ref enforced_import_renames,
enum_variant_name_threshold,
enum_variant_size_threshold,
excessive_nesting_threshold,
future_size_threshold,
ref ignore_interior_mutability,
large_error_threshold,
literal_representation_threshold,
matches_for_let_else,
max_fn_params_bools,
max_include_file_size,
max_struct_bools,
max_suggested_slice_pattern_length,
max_trait_bounds,
min_ident_chars_threshold,
missing_docs_in_crate_items,
ref msrv,
pass_by_value_size_limit,
semicolon_inside_block_ignore_singleline,
semicolon_outside_block_ignore_multiline,
single_char_binding_names_threshold,
stack_size_threshold,
ref standard_macro_braces,
struct_field_name_threshold,
suppress_restriction_lint_in_const,
too_large_for_stack,
too_many_arguments_threshold,
too_many_lines_threshold,
trivial_copy_size_limit,
type_complexity_threshold,
unnecessary_box_size,
unreadable_literal_lint_fractions,
upper_case_acronyms_aggressive,
vec_box_size_threshold,
verbose_bit_mask_threshold,
warn_on_all_wildcard_imports,
blacklisted_names: _,
cyclomatic_complexity_threshold: _,
} = *conf;
let msrv = || msrv.clone();
register_removed_non_tool_lints(store); register_removed_non_tool_lints(store);
register_categories(store); register_categories(store);
@ -521,7 +592,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| { store.register_late_pass(|_| {
Box::new(utils::internal_lints::compiler_lint_functions::CompilerLintFunctions::new()) Box::new(utils::internal_lints::compiler_lint_functions::CompilerLintFunctions::new())
}); });
store.register_late_pass(|_| Box::new(utils::internal_lints::if_chain_style::IfChainStyle));
store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths)); store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths));
store.register_late_pass(|_| { store.register_late_pass(|_| {
Box::<utils::internal_lints::interning_defined_symbol::InterningDefinedSymbol>::default() Box::<utils::internal_lints::interning_defined_symbol::InterningDefinedSymbol>::default()
@ -537,9 +607,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
}); });
} }
let arithmetic_side_effects_allowed = conf.arithmetic_side_effects_allowed.clone();
let arithmetic_side_effects_allowed_binary = conf.arithmetic_side_effects_allowed_binary.clone();
let arithmetic_side_effects_allowed_unary = conf.arithmetic_side_effects_allowed_unary.clone();
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new( Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(
arithmetic_side_effects_allowed arithmetic_side_effects_allowed
@ -557,16 +624,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(|| Box::<utils::format_args_collector::FormatArgsCollector>::default()); store.register_early_pass(|| Box::<utils::format_args_collector::FormatArgsCollector>::default());
store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
store.register_late_pass(|_| Box::new(utils::author::Author)); store.register_late_pass(|_| Box::new(utils::author::Author));
let await_holding_invalid_types = conf.await_holding_invalid_types.clone();
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(await_holding_invalid::AwaitHolding::new( Box::new(await_holding_invalid::AwaitHolding::new(
await_holding_invalid_types.clone(), await_holding_invalid_types.clone(),
)) ))
}); });
store.register_late_pass(|_| Box::new(serde_api::SerdeApi)); store.register_late_pass(|_| Box::new(serde_api::SerdeApi));
let vec_box_size_threshold = conf.vec_box_size_threshold;
let type_complexity_threshold = conf.type_complexity_threshold;
let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(types::Types::new( Box::new(types::Types::new(
vec_box_size_threshold, vec_box_size_threshold,
@ -599,19 +662,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor)); store.register_late_pass(|_| Box::new(inconsistent_struct_constructor::InconsistentStructConstructor));
store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions)); store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports)); store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
let msrv = || conf.msrv.clone();
let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
let allow_expect_in_tests = conf.allow_expect_in_tests;
let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
let suppress_restriction_lint_in_const = conf.suppress_restriction_lint_in_const;
store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv()))); store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv())));
let allowed_dotfiles = conf
.allowed_dotfiles
.iter()
.cloned()
.chain(methods::DEFAULT_ALLOWED_DOTFILES.iter().copied().map(ToOwned::to_owned))
.collect::<FxHashSet<_>>();
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(methods::Methods::new( Box::new(methods::Methods::new(
avoid_breaking_exported_api, avoid_breaking_exported_api,
@ -622,7 +673,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
)) ))
}); });
store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv()))); store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv())));
let matches_for_let_else = conf.matches_for_let_else;
store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv()))); store.register_early_pass(move || Box::new(manual_non_exhaustive::ManualNonExhaustiveStruct::new(msrv())));
store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv()))); store.register_late_pass(move |_| Box::new(manual_non_exhaustive::ManualNonExhaustiveEnum::new(msrv())));
store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv()))); store.register_late_pass(move |_| Box::new(manual_strip::ManualStrip::new(msrv())));
@ -639,7 +689,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv()))); store.register_early_pass(move || Box::new(unnested_or_patterns::UnnestedOrPatterns::new(msrv())));
store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount)); store.register_late_pass(|_| Box::new(size_of_in_element_count::SizeOfInElementCount));
store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod)); store.register_late_pass(|_| Box::new(same_name_method::SameNameMethod));
let max_suggested_slice_pattern_length = conf.max_suggested_slice_pattern_length;
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(index_refutable_slice::IndexRefutableSlice::new( Box::new(index_refutable_slice::IndexRefutableSlice::new(
max_suggested_slice_pattern_length, max_suggested_slice_pattern_length,
@ -648,7 +697,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
}); });
store.register_late_pass(|_| Box::<shadow::Shadow>::default()); store.register_late_pass(|_| Box::<shadow::Shadow>::default());
store.register_late_pass(|_| Box::new(unit_types::UnitTypes)); store.register_late_pass(|_| Box::new(unit_types::UnitTypes));
let enforce_iter_loop_reborrow = conf.enforce_iter_loop_reborrow;
store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv(), enforce_iter_loop_reborrow))); store.register_late_pass(move |_| Box::new(loops::Loops::new(msrv(), enforce_iter_loop_reborrow)));
store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default()); store.register_late_pass(|_| Box::<main_recursion::MainRecursion>::default());
store.register_late_pass(|_| Box::new(lifetimes::Lifetimes)); store.register_late_pass(|_| Box::new(lifetimes::Lifetimes));
@ -662,13 +710,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(no_effect::NoEffect)); store.register_late_pass(|_| Box::new(no_effect::NoEffect));
store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment)); store.register_late_pass(|_| Box::new(temporary_assignment::TemporaryAssignment));
store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv()))); store.register_late_pass(move |_| Box::new(transmute::Transmute::new(msrv())));
let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(cognitive_complexity::CognitiveComplexity::new( Box::new(cognitive_complexity::CognitiveComplexity::new(
cognitive_complexity_threshold, cognitive_complexity_threshold,
)) ))
}); });
let too_large_for_stack = conf.too_large_for_stack;
store.register_late_pass(move |_| Box::new(escape::BoxedLocal { too_large_for_stack })); store.register_late_pass(move |_| Box::new(escape::BoxedLocal { too_large_for_stack }));
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(vec::UselessVec { Box::new(vec::UselessVec {
@ -684,18 +730,13 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum)); store.register_late_pass(|_| Box::new(empty_enum::EmptyEnum));
store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons)); store.register_late_pass(|_| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
store.register_late_pass(|_| Box::<regex::Regex>::default()); store.register_late_pass(|_| Box::<regex::Regex>::default());
let ignore_interior_mutability = conf.ignore_interior_mutability.clone();
store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone()))); store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone())));
store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator)); store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator));
store.register_late_pass(|_| Box::new(format::UselessFormat)); store.register_late_pass(|_| Box::new(format::UselessFormat));
store.register_late_pass(|_| Box::new(swap::Swap)); store.register_late_pass(|_| Box::new(swap::Swap));
store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional)); store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional));
store.register_late_pass(|_| Box::<new_without_default::NewWithoutDefault>::default()); store.register_late_pass(|_| Box::<new_without_default::NewWithoutDefault>::default());
let disallowed_names = conf.disallowed_names.iter().cloned().collect::<FxHashSet<_>>(); store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names)));
store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names.clone())));
let too_many_arguments_threshold = conf.too_many_arguments_threshold;
let too_many_lines_threshold = conf.too_many_lines_threshold;
let large_error_threshold = conf.large_error_threshold;
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(functions::Functions::new( Box::new(functions::Functions::new(
too_many_arguments_threshold, too_many_arguments_threshold,
@ -704,9 +745,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
avoid_breaking_exported_api, avoid_breaking_exported_api,
)) ))
}); });
let doc_valid_idents = conf.doc_valid_idents.iter().cloned().collect::<FxHashSet<_>>(); store.register_late_pass(move |_| Box::new(doc::DocMarkdown::new(doc_valid_idents)));
let missing_docs_in_crate_items = conf.missing_docs_in_crate_items;
store.register_late_pass(move |_| Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply)); store.register_late_pass(|_| Box::new(neg_multiply::NegMultiply));
store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq)); store.register_late_pass(|_| Box::new(let_if_seq::LetIfSeq));
store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence)); store.register_late_pass(|_| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
@ -716,17 +755,17 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk)); store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk));
store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl)); store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl));
store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount)); store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount));
let enum_variant_size_threshold = conf.enum_variant_size_threshold;
store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold))); store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)));
store.register_late_pass(|_| Box::new(explicit_write::ExplicitWrite)); store.register_late_pass(|_| Box::new(explicit_write::ExplicitWrite));
store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue)); store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue));
let pass_by_ref_or_value = pass_by_ref_or_value::PassByRefOrValue::new( store.register_late_pass(move |tcx| {
conf.trivial_copy_size_limit, Box::new(pass_by_ref_or_value::PassByRefOrValue::new(
conf.pass_by_value_size_limit, trivial_copy_size_limit,
conf.avoid_breaking_exported_api, pass_by_value_size_limit,
&sess.target, avoid_breaking_exported_api,
); tcx.sess.target.pointer_width,
store.register_late_pass(move |_| Box::new(pass_by_ref_or_value)); ))
});
store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef)); store.register_late_pass(|_| Box::new(ref_option_ref::RefOptionRef));
store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter)); store.register_late_pass(|_| Box::new(infinite_iter::InfiniteIter));
store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody)); store.register_late_pass(|_| Box::new(inline_fn_without_body::InlineFnWithoutBody));
@ -746,7 +785,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
suppress_restriction_lint_in_const, suppress_restriction_lint_in_const,
)) ))
}); });
let ignore_interior_mutability = conf.ignore_interior_mutability.clone();
store.register_late_pass(move |_| Box::new(non_copy_const::NonCopyConst::new(ignore_interior_mutability.clone()))); store.register_late_pass(move |_| Box::new(non_copy_const::NonCopyConst::new(ignore_interior_mutability.clone())));
store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast)); store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone)); store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone));
@ -755,10 +793,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants)); store.register_late_pass(|_| Box::new(assertions_on_constants::AssertionsOnConstants));
store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates)); store.register_late_pass(|_| Box::new(assertions_on_result_states::AssertionsOnResultStates));
store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString)); store.register_late_pass(|_| Box::new(inherent_to_string::InherentToString));
let max_trait_bounds = conf.max_trait_bounds;
store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds, msrv()))); store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(max_trait_bounds, msrv())));
store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain)); store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain));
let ignore_interior_mutability = conf.ignore_interior_mutability.clone();
store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone()))); store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone())));
store.register_early_pass(|| Box::new(reference::DerefAddrOf)); store.register_early_pass(|| Box::new(reference::DerefAddrOf));
store.register_early_pass(|| Box::new(double_parens::DoubleParens)); store.register_early_pass(|| Box::new(double_parens::DoubleParens));
@ -779,21 +815,16 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_early_pass(|| Box::new(redundant_else::RedundantElse)); store.register_early_pass(|| Box::new(redundant_else::RedundantElse));
store.register_late_pass(|_| Box::new(create_dir::CreateDir)); store.register_late_pass(|_| Box::new(create_dir::CreateDir));
store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType)); store.register_early_pass(|| Box::new(needless_arbitrary_self_type::NeedlessArbitrarySelfType));
let literal_representation_lint_fraction_readability = conf.unreadable_literal_lint_fractions;
store.register_early_pass(move || { store.register_early_pass(move || {
Box::new(literal_representation::LiteralDigitGrouping::new( Box::new(literal_representation::LiteralDigitGrouping::new(
literal_representation_lint_fraction_readability, unreadable_literal_lint_fractions,
)) ))
}); });
let literal_representation_threshold = conf.literal_representation_threshold;
store.register_early_pass(move || { store.register_early_pass(move || {
Box::new(literal_representation::DecimalLiteralRepresentation::new( Box::new(literal_representation::DecimalLiteralRepresentation::new(
literal_representation_threshold, literal_representation_threshold,
)) ))
}); });
let enum_variant_name_threshold = conf.enum_variant_name_threshold;
let struct_field_name_threshold = conf.struct_field_name_threshold;
let allow_private_module_inception = conf.allow_private_module_inception;
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(item_name_repetitions::ItemNameRepetitions::new( Box::new(item_name_repetitions::ItemNameRepetitions::new(
enum_variant_name_threshold, enum_variant_name_threshold,
@ -803,7 +834,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
)) ))
}); });
store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments)); store.register_early_pass(|| Box::new(tabs_in_doc_comments::TabsInDocComments));
let upper_case_acronyms_aggressive = conf.upper_case_acronyms_aggressive;
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(upper_case_acronyms::UpperCaseAcronyms::new( Box::new(upper_case_acronyms::UpperCaseAcronyms::new(
avoid_breaking_exported_api, avoid_breaking_exported_api,
@ -815,15 +845,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall)); store.register_late_pass(|_| Box::new(mutable_debug_assertion::DebugAssertWithMutCall));
store.register_late_pass(|_| Box::new(exit::Exit)); store.register_late_pass(|_| Box::new(exit::Exit));
store.register_late_pass(|_| Box::new(to_digit_is_some::ToDigitIsSome)); store.register_late_pass(|_| Box::new(to_digit_is_some::ToDigitIsSome));
let array_size_threshold = u128::from(conf.array_size_threshold); store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold.into())));
store.register_late_pass(move |_| Box::new(large_stack_arrays::LargeStackArrays::new(array_size_threshold))); store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold.into())));
store.register_late_pass(move |_| Box::new(large_const_arrays::LargeConstArrays::new(array_size_threshold)));
store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic)); store.register_late_pass(|_| Box::new(floating_point_arithmetic::FloatingPointArithmetic));
store.register_late_pass(|_| Box::new(as_conversions::AsConversions)); store.register_late_pass(|_| Box::new(as_conversions::AsConversions));
store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore)); store.register_late_pass(|_| Box::new(let_underscore::LetUnderscore));
store.register_early_pass(|| Box::<single_component_path_imports::SingleComponentPathImports>::default()); store.register_early_pass(|| Box::<single_component_path_imports::SingleComponentPathImports>::default());
let max_fn_params_bools = conf.max_fn_params_bools;
let max_struct_bools = conf.max_struct_bools;
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(excessive_bools::ExcessiveBools::new( Box::new(excessive_bools::ExcessiveBools::new(
max_struct_bools, max_struct_bools,
@ -831,36 +858,30 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
)) ))
}); });
store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap)); store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
let warn_on_all_wildcard_imports = conf.warn_on_all_wildcard_imports;
store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports))); store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default()); store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress)); store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress));
store.register_late_pass(|_| Box::<dereference::Dereferencing<'_>>::default()); store.register_late_pass(|_| Box::<dereference::Dereferencing<'_>>::default());
store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse)); store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend)); store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
let future_size_threshold = conf.future_size_threshold;
store.register_late_pass(move |_| Box::new(large_futures::LargeFuture::new(future_size_threshold))); store.register_late_pass(move |_| Box::new(large_futures::LargeFuture::new(future_size_threshold)));
store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex)); store.register_late_pass(|_| Box::new(if_let_mutex::IfLetMutex));
store.register_late_pass(|_| Box::new(if_not_else::IfNotElse)); store.register_late_pass(|_| Box::new(if_not_else::IfNotElse));
store.register_late_pass(|_| Box::new(equatable_if_let::PatternEquality)); store.register_late_pass(|_| Box::new(equatable_if_let::PatternEquality));
store.register_late_pass(|_| Box::new(manual_async_fn::ManualAsyncFn)); store.register_late_pass(|_| Box::new(manual_async_fn::ManualAsyncFn));
store.register_late_pass(|_| Box::new(panic_in_result_fn::PanicInResultFn)); store.register_late_pass(|_| Box::new(panic_in_result_fn::PanicInResultFn));
let single_char_binding_names_threshold = conf.single_char_binding_names_threshold;
store.register_early_pass(move || { store.register_early_pass(move || {
Box::new(non_expressive_names::NonExpressiveNames { Box::new(non_expressive_names::NonExpressiveNames {
single_char_binding_names_threshold, single_char_binding_names_threshold,
}) })
}); });
let macro_matcher = conf.standard_macro_braces.iter().cloned().collect::<FxHashSet<_>>(); store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(standard_macro_braces)));
store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(&macro_matcher)));
store.register_late_pass(|_| Box::<macro_use::MacroUseImports>::default()); store.register_late_pass(|_| Box::<macro_use::MacroUseImports>::default());
store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch)); store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch));
store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult)); store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult));
store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned)); store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync)); store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync));
let disallowed_macros = conf.disallowed_macros.clone();
store.register_late_pass(move |_| Box::new(disallowed_macros::DisallowedMacros::new(disallowed_macros.clone()))); store.register_late_pass(move |_| Box::new(disallowed_macros::DisallowedMacros::new(disallowed_macros.clone())));
let disallowed_methods = conf.disallowed_methods.clone();
store.register_late_pass(move |_| Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone()))); store.register_late_pass(move |_| Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone())));
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax)); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax)); store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
@ -875,36 +896,30 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison)); store.register_late_pass(|_| Box::new(bool_assert_comparison::BoolAssertComparison));
store.register_early_pass(move || Box::new(module_style::ModStyle)); store.register_early_pass(move || Box::new(module_style::ModStyle));
store.register_late_pass(|_| Box::<unused_async::UnusedAsync>::default()); store.register_late_pass(|_| Box::<unused_async::UnusedAsync>::default());
let disallowed_types = conf.disallowed_types.clone();
store.register_late_pass(move |_| Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone()))); store.register_late_pass(move |_| Box::new(disallowed_types::DisallowedTypes::new(disallowed_types.clone())));
let import_renames = conf.enforced_import_renames.clone();
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(missing_enforced_import_rename::ImportRename::new( Box::new(missing_enforced_import_rename::ImportRename::new(
import_renames.clone(), enforced_import_renames.clone(),
)) ))
}); });
let scripts = conf.allowed_scripts.clone(); store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(allowed_scripts)));
store.register_early_pass(move || Box::new(disallowed_script_idents::DisallowedScriptIdents::new(&scripts)));
store.register_late_pass(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings)); store.register_late_pass(|_| Box::new(strlen_on_c_strings::StrlenOnCStrings));
store.register_late_pass(move |_| Box::new(self_named_constructors::SelfNamedConstructors)); store.register_late_pass(move |_| Box::new(self_named_constructors::SelfNamedConstructors));
store.register_late_pass(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator)); store.register_late_pass(move |_| Box::new(iter_not_returning_iterator::IterNotReturningIterator));
store.register_late_pass(move |_| Box::new(manual_assert::ManualAssert)); store.register_late_pass(move |_| Box::new(manual_assert::ManualAssert));
let enable_raw_pointer_heuristic_for_send = conf.enable_raw_pointer_heuristic_for_send;
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new( Box::new(non_send_fields_in_send_ty::NonSendFieldInSendTy::new(
enable_raw_pointer_heuristic_for_send, enable_raw_pointer_heuristic_for_send,
)) ))
}); });
let accept_comment_above_statement = conf.accept_comment_above_statement;
let accept_comment_above_attributes = conf.accept_comment_above_attributes;
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new( Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(
accept_comment_above_statement, accept_comment_above_statement,
accept_comment_above_attributes, accept_comment_above_attributes,
)) ))
}); });
let allow_mixed_uninlined = conf.allow_mixed_uninlined_format_args; store
store.register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined))); .register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined_format_args)));
store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray)); store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray));
store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes)); store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit)); store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit));
@ -914,11 +929,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv()))); store.register_late_pass(move |_| Box::new(manual_bits::ManualBits::new(msrv())));
store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation)); store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation));
store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default()); store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
let allow_dbg_in_tests = conf.allow_dbg_in_tests;
store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests))); store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
let allow_print_in_tests = conf.allow_print_in_tests;
store.register_late_pass(move |_| Box::new(write::Write::new(allow_print_in_tests))); store.register_late_pass(move |_| Box::new(write::Write::new(allow_print_in_tests)));
let cargo_ignore_publish = conf.cargo_ignore_publish;
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(cargo::Cargo { Box::new(cargo::Cargo {
ignore_publish: cargo_ignore_publish, ignore_publish: cargo_ignore_publish,
@ -929,7 +941,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings)); store.register_late_pass(|_| Box::new(unnecessary_owned_empty_strings::UnnecessaryOwnedEmptyStrings));
store.register_early_pass(|| Box::new(pub_use::PubUse)); store.register_early_pass(|| Box::new(pub_use::PubUse));
store.register_late_pass(|_| Box::new(format_push_string::FormatPushString)); store.register_late_pass(|_| Box::new(format_push_string::FormatPushString));
let max_include_file_size = conf.max_include_file_size;
store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size))); store.register_late_pass(move |_| Box::new(large_include_file::LargeIncludeFile::new(max_include_file_size)));
store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace)); store.register_late_pass(|_| Box::new(strings::TrimSplitWhitespace));
store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit)); store.register_late_pass(|_| Box::new(rc_clone_in_vec_init::RcCloneInVecInit));
@ -942,7 +953,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty)); store.register_late_pass(|_| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv()))); store.register_late_pass(move |_| Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv())));
store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv()))); store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(msrv())));
let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold))); store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default()); store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv()))); store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv())));
@ -959,8 +969,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr)); store.register_late_pass(|_| Box::new(from_raw_with_void_ptr::FromRawWithVoidPtr));
store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow)); store.register_late_pass(|_| Box::new(suspicious_xor_used_as_pow::ConfusingXorAndPow));
store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv()))); store.register_late_pass(move |_| Box::new(manual_is_ascii_check::ManualIsAsciiCheck::new(msrv())));
let semicolon_inside_block_ignore_singleline = conf.semicolon_inside_block_ignore_singleline;
let semicolon_outside_block_ignore_multiline = conf.semicolon_outside_block_ignore_multiline;
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(semicolon_block::SemicolonBlock::new( Box::new(semicolon_block::SemicolonBlock::new(
semicolon_inside_block_ignore_singleline, semicolon_inside_block_ignore_singleline,
@ -983,7 +991,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute)); store.register_late_pass(|_| Box::new(allow_attributes::AllowAttribute));
store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(msrv()))); store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(msrv())));
store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct)); store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct));
let unnecessary_box_size = conf.unnecessary_box_size;
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new( Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(
avoid_breaking_exported_api, avoid_breaking_exported_api,
@ -993,8 +1000,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(lines_filter_map_ok::LinesFilterMapOk)); store.register_late_pass(|_| Box::new(lines_filter_map_ok::LinesFilterMapOk));
store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule)); store.register_late_pass(|_| Box::new(tests_outside_test_module::TestsOutsideTestModule));
store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation)); store.register_late_pass(|_| Box::new(manual_slice_size_calculation::ManualSliceSizeCalculation));
store.register_early_pass(|| Box::new(suspicious_doc_comments::SuspiciousDocComments));
let excessive_nesting_threshold = conf.excessive_nesting_threshold;
store.register_early_pass(move || { store.register_early_pass(move || {
Box::new(excessive_nesting::ExcessiveNesting { Box::new(excessive_nesting::ExcessiveNesting {
excessive_nesting_threshold, excessive_nesting_threshold,
@ -1010,15 +1015,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations)); store.register_late_pass(|_| Box::new(redundant_type_annotations::RedundantTypeAnnotations));
store.register_late_pass(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync)); store.register_late_pass(|_| Box::new(arc_with_non_send_sync::ArcWithNonSendSync));
store.register_late_pass(|_| Box::new(needless_if::NeedlessIf)); store.register_late_pass(|_| Box::new(needless_if::NeedlessIf));
let allowed_idents_below_min_chars = conf.allowed_idents_below_min_chars.clone();
let min_ident_chars_threshold = conf.min_ident_chars_threshold;
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(min_ident_chars::MinIdentChars { Box::new(min_ident_chars::MinIdentChars {
allowed_idents_below_min_chars: allowed_idents_below_min_chars.clone(), allowed_idents_below_min_chars: allowed_idents_below_min_chars.clone(),
min_ident_chars_threshold, min_ident_chars_threshold,
}) })
}); });
let stack_size_threshold = conf.stack_size_threshold;
store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(stack_size_threshold))); store.register_late_pass(move |_| Box::new(large_stack_frames::LargeStackFrames::new(stack_size_threshold)));
store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit)); store.register_late_pass(|_| Box::new(single_range_in_vec_init::SingleRangeInVecInit));
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
@ -1033,10 +1035,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
def_id_to_usage: rustc_data_structures::fx::FxHashMap::default(), def_id_to_usage: rustc_data_structures::fx::FxHashMap::default(),
}) })
}); });
let needless_raw_string_hashes_allow_one = conf.allow_one_hash_in_raw_strings;
store.register_early_pass(move || { store.register_early_pass(move || {
Box::new(raw_strings::RawStrings { Box::new(raw_strings::RawStrings {
needless_raw_string_hashes_allow_one, allow_one_hash_in_raw_strings,
}) })
}); });
store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns)); store.register_late_pass(|_| Box::new(manual_range_patterns::ManualRangePatterns));
@ -1045,8 +1046,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods)); store.register_late_pass(|_| Box::new(manual_float_methods::ManualFloatMethods));
store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes)); store.register_late_pass(|_| Box::new(four_forward_slashes::FourForwardSlashes));
store.register_late_pass(|_| Box::new(error_impl_error::ErrorImplError)); store.register_late_pass(|_| Box::new(error_impl_error::ErrorImplError));
let absolute_paths_max_segments = conf.absolute_paths_max_segments;
let absolute_paths_allowed_crates = conf.absolute_paths_allowed_crates.clone();
store.register_late_pass(move |_| { store.register_late_pass(move |_| {
Box::new(absolute_paths::AbsolutePaths { Box::new(absolute_paths::AbsolutePaths {
absolute_paths_max_segments, absolute_paths_max_segments,
@ -1066,6 +1065,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
}); });
store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(msrv()))); store.register_late_pass(move |_| Box::new(manual_hash_one::ManualHashOne::new(msrv())));
store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter)); store.register_late_pass(|_| Box::new(iter_without_into_iter::IterWithoutIntoIter));
store.register_late_pass(|_| Box::new(iter_over_hash_type::IterOverHashType));
// add lints here, do not remove this comment, it's used in `new_lint` // add lints here, do not remove this comment, it's used in `new_lint`
} }

View file

@ -20,8 +20,8 @@ use rustc_middle::hir::nested_filter as middle_nested_filter;
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint}; use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::def_id::LocalDefId; use rustc_span::def_id::LocalDefId;
use rustc_span::Span;
use rustc_span::symbol::{kw, Ident, Symbol}; use rustc_span::symbol::{kw, Ident, Symbol};
use rustc_span::Span;
declare_clippy_lint! { declare_clippy_lint! {
/// ### What it does /// ### What it does
@ -310,13 +310,11 @@ fn elision_suggestions(
// elision doesn't work for explicit self types, see rust-lang/rust#69064 // elision doesn't work for explicit self types, see rust-lang/rust#69064
fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool { fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident: Option<Ident>) -> bool {
if_chain! { if let Some(ident) = ident
if let Some(ident) = ident; && ident.name == kw::SelfLower
if ident.name == kw::SelfLower; && !func.implicit_self.has_implicit_self()
if !func.implicit_self.has_implicit_self(); && let Some(self_ty) = func.inputs.first()
{
if let Some(self_ty) = func.inputs.first();
then {
let mut visitor = RefVisitor::new(cx); let mut visitor = RefVisitor::new(cx);
visitor.visit_ty(self_ty); visitor.visit_ty(self_ty);
@ -324,7 +322,6 @@ fn explicit_self_type<'tcx>(cx: &LateContext<'tcx>, func: &FnDecl<'tcx>, ident:
} else { } else {
false false
} }
}
} }
fn named_lifetime(lt: &Lifetime) -> Option<LocalDefId> { fn named_lifetime(lt: &Lifetime) -> Option<LocalDefId> {

View file

@ -4,7 +4,6 @@
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::numeric_literal::{NumericLiteral, Radix}; use clippy_utils::numeric_literal::{NumericLiteral, Radix};
use clippy_utils::source::snippet_opt; use clippy_utils::source::snippet_opt;
use if_chain::if_chain;
use rustc_ast::ast::{Expr, ExprKind, LitKind}; use rustc_ast::ast::{Expr, ExprKind, LitKind};
use rustc_ast::token; use rustc_ast::token;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -255,11 +254,10 @@ impl LiteralDigitGrouping {
} }
fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) {
if_chain! { if let Some(src) = snippet_opt(cx, span)
if let Some(src) = snippet_opt(cx, span); && let Ok(lit_kind) = LitKind::from_token_lit(lit)
if let Ok(lit_kind) = LitKind::from_token_lit(lit); && let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind)
if let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind); {
then {
if !Self::check_for_mistyped_suffix(cx, span, &mut num_lit) { if !Self::check_for_mistyped_suffix(cx, span, &mut num_lit) {
return; return;
} }
@ -269,18 +267,17 @@ impl LiteralDigitGrouping {
} }
let result = (|| { let result = (|| {
let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix, true)?; let integral_group_size = Self::get_group_size(num_lit.integer.split('_'), num_lit.radix, true)?;
if let Some(fraction) = num_lit.fraction { if let Some(fraction) = num_lit.fraction {
let fractional_group_size = Self::get_group_size( let fractional_group_size =
fraction.rsplit('_'), Self::get_group_size(fraction.rsplit('_'), num_lit.radix, self.lint_fraction_readability)?;
num_lit.radix,
self.lint_fraction_readability)?;
let consistent = Self::parts_consistent(integral_group_size, let consistent = Self::parts_consistent(
integral_group_size,
fractional_group_size, fractional_group_size,
num_lit.integer.len(), num_lit.integer.len(),
fraction.len()); fraction.len(),
);
if !consistent { if !consistent {
return Err(WarningType::InconsistentDigitGrouping); return Err(WarningType::InconsistentDigitGrouping);
}; };
@ -289,18 +286,13 @@ impl LiteralDigitGrouping {
Ok(()) Ok(())
})(); })();
if let Err(warning_type) = result { if let Err(warning_type) = result {
let should_warn = match warning_type { let should_warn = match warning_type {
| WarningType::UnreadableLiteral WarningType::UnreadableLiteral
| WarningType::InconsistentDigitGrouping | WarningType::InconsistentDigitGrouping
| WarningType::UnusualByteGroupings | WarningType::UnusualByteGroupings
| WarningType::LargeDigitGroups => { | WarningType::LargeDigitGroups => !span.from_expansion(),
!span.from_expansion() WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => true,
}
WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => {
true
}
}; };
if should_warn { if should_warn {
warning_type.display(num_lit.format(), cx, span); warning_type.display(num_lit.format(), cx, span);
@ -308,7 +300,6 @@ impl LiteralDigitGrouping {
} }
} }
} }
}
// Returns `false` if the check fails // Returns `false` if the check fails
fn check_for_mistyped_suffix( fn check_for_mistyped_suffix(
@ -478,14 +469,13 @@ impl DecimalLiteralRepresentation {
} }
fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { fn check_lit(self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) {
// Lint integral literals. // Lint integral literals.
if_chain! { if let Ok(lit_kind) = LitKind::from_token_lit(lit)
if let Ok(lit_kind) = LitKind::from_token_lit(lit); && let LitKind::Int(val, _) = lit_kind
if let LitKind::Int(val, _) = lit_kind; && let Some(src) = snippet_opt(cx, span)
if let Some(src) = snippet_opt(cx, span); && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind)
if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind); && num_lit.radix == Radix::Decimal
if num_lit.radix == Radix::Decimal; && val >= u128::from(self.threshold)
if val >= u128::from(self.threshold); {
then {
let hex = format!("{val:#X}"); let hex = format!("{val:#X}");
let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false);
let _: Result<(), ()> = Self::do_lint(num_lit.integer).map_err(|warning_type| { let _: Result<(), ()> = Self::do_lint(num_lit.integer).map_err(|warning_type| {
@ -493,7 +483,6 @@ impl DecimalLiteralRepresentation {
}); });
} }
} }
}
fn do_lint(digits: &str) -> Result<(), WarningType> { fn do_lint(digits: &str) -> Result<(), WarningType> {
if digits.len() == 1 { if digits.len() == 1 {

View file

@ -2,7 +2,6 @@ use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT
use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{get_enclosing_block, is_integer_const}; use clippy_utils::{get_enclosing_block, is_integer_const};
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_block, walk_expr}; use rustc_hir::intravisit::{walk_block, walk_expr};
use rustc_hir::{Expr, Pat}; use rustc_hir::{Expr, Pat};
@ -30,10 +29,9 @@ pub(super) fn check<'tcx>(
let mut initialize_visitor = InitializeVisitor::new(cx, expr, id); let mut initialize_visitor = InitializeVisitor::new(cx, expr, id);
walk_block(&mut initialize_visitor, block); walk_block(&mut initialize_visitor, block);
if_chain! { if let Some((name, ty, initializer)) = initialize_visitor.get_result()
if let Some((name, ty, initializer)) = initialize_visitor.get_result(); && is_integer_const(cx, initializer, 0)
if is_integer_const(cx, initializer, 0); {
then {
let mut applicability = Applicability::MaybeIncorrect; let mut applicability = Applicability::MaybeIncorrect;
let span = expr.span.with_hi(arg.span.hi()); let span = expr.span.with_hi(arg.span.hi());
@ -54,7 +52,7 @@ pub(super) fn check<'tcx>(
applicability, applicability,
); );
return; return;
} },
Some(ty::Int(int_ty)) => int_ty.name_str(), Some(ty::Int(int_ty)) => int_ty.name_str(),
Some(ty::Uint(uint_ty)) => uint_ty.name_str(), Some(ty::Uint(uint_ty)) => uint_ty.name_str(),
_ => return, _ => return,
@ -85,5 +83,4 @@ pub(super) fn check<'tcx>(
} }
} }
} }
}
} }

View file

@ -4,7 +4,6 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::implements_trait; use clippy_utils::ty::implements_trait;
use clippy_utils::{higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt}; use clippy_utils::{higher, is_res_lang_ctor, path_res, peel_blocks_with_stmt};
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::Res; use rustc_hir::def::Res;
use rustc_hir::lang_items::LangItem; use rustc_hir::lang_items::LangItem;
@ -23,18 +22,21 @@ pub(super) fn check<'tcx>(
let inner_expr = peel_blocks_with_stmt(body); let inner_expr = peel_blocks_with_stmt(body);
// Check for the specific case that the result is returned and optimize suggestion for that (more // Check for the specific case that the result is returned and optimize suggestion for that (more
// cases can be added later) // cases can be added later)
if_chain! { if let Some(higher::If {
if let Some(higher::If { cond, then, r#else: None, }) = higher::If::hir(inner_expr); cond,
if let Some(binding_id) = get_binding(pat); then,
if let ExprKind::Block(block, _) = then.kind; r#else: None,
if let [stmt] = block.stmts; }) = higher::If::hir(inner_expr)
if let StmtKind::Semi(semi) = stmt.kind; && let Some(binding_id) = get_binding(pat)
if let ExprKind::Ret(Some(ret_value)) = semi.kind; && let ExprKind::Block(block, _) = then.kind
if let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind; && let [stmt] = block.stmts
if is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome); && let StmtKind::Semi(semi) = stmt.kind
if path_res(cx, inner_ret) == Res::Local(binding_id); && let ExprKind::Ret(Some(ret_value)) = semi.kind
if let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr); && let ExprKind::Call(ctor, [inner_ret]) = ret_value.kind
then { && is_res_lang_ctor(cx, path_res(cx, ctor), LangItem::OptionSome)
&& path_res(cx, inner_ret) == Res::Local(binding_id)
&& let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr)
{
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let mut snippet = make_iterator_snippet(cx, arg, &mut applicability); let mut snippet = make_iterator_snippet(cx, arg, &mut applicability);
// Checks if `pat` is a single reference to a binding (`&x`) // Checks if `pat` is a single reference to a binding (`&x`)
@ -52,7 +54,12 @@ pub(super) fn check<'tcx>(
); );
} }
let ty = cx.typeck_results().expr_ty(inner_ret); let ty = cx.typeck_results().expr_ty(inner_ret);
if cx.tcx.lang_items().copy_trait().map_or(false, |id| implements_trait(cx, ty, id, &[])) { if cx
.tcx
.lang_items()
.copy_trait()
.map_or(false, |id| implements_trait(cx, ty, id, &[]))
{
snippet.push_str( snippet.push_str(
&format!( &format!(
".find(|{}{}| {})", ".find(|{}{}| {})",
@ -85,16 +92,10 @@ pub(super) fn check<'tcx>(
if applicability == Applicability::MaybeIncorrect { if applicability == Applicability::MaybeIncorrect {
diag.note("you may need to dereference some variables"); diag.note("you may need to dereference some variables");
} }
diag.span_suggestion( diag.span_suggestion(lint_span, "replace with an iterator", snippet, applicability);
lint_span,
"replace with an iterator",
snippet,
applicability,
);
}, },
); );
} }
}
} }
fn get_binding(pat: &Pat<'_>) -> Option<HirId> { fn get_binding(pat: &Pat<'_>) -> Option<HirId> {
@ -124,34 +125,30 @@ fn last_stmt_and_ret<'tcx>(
if let Some(ret) = block.expr { if let Some(ret) = block.expr {
return Some((last_stmt, ret)); return Some((last_stmt, ret));
} }
if_chain! { if let [.., snd_last, _] = block.stmts
if let [.., snd_last, _] = block.stmts; && let StmtKind::Semi(last_expr) = last_stmt.kind
if let StmtKind::Semi(last_expr) = last_stmt.kind; && let ExprKind::Ret(Some(ret)) = last_expr.kind
if let ExprKind::Ret(Some(ret)) = last_expr.kind; {
then {
return Some((snd_last, ret)); return Some((snd_last, ret));
} }
} }
}
None None
} }
let mut parent_iter = cx.tcx.hir().parent_iter(expr.hir_id); let mut parent_iter = cx.tcx.hir().parent_iter(expr.hir_id);
if_chain! { if let Some((node_hir, Node::Stmt(..))) = parent_iter.next()
// This should be the loop // This should be the loop
if let Some((node_hir, Node::Stmt(..))) = parent_iter.next();
// This should be the function body // This should be the function body
if let Some((_, Node::Block(block))) = parent_iter.next(); && let Some((_, Node::Block(block))) = parent_iter.next()
if let Some((last_stmt, last_ret)) = extract(block); && let Some((last_stmt, last_ret)) = extract(block)
if last_stmt.hir_id == node_hir; && last_stmt.hir_id == node_hir
if is_res_lang_ctor(cx, path_res(cx, last_ret), LangItem::OptionNone); && is_res_lang_ctor(cx, path_res(cx, last_ret), LangItem::OptionNone)
if let Some((_, Node::Expr(_block))) = parent_iter.next(); && let Some((_, Node::Expr(_block))) = parent_iter.next()
// This includes the function header // This includes the function header
if let Some((_, func)) = parent_iter.next(); && let Some((_, func)) = parent_iter.next()
if func.fn_kind().is_some(); && func.fn_kind().is_some()
then { {
Some((block.stmts.last().unwrap(), last_ret)) Some((block.stmts.last().unwrap(), last_ret))
} else { } else {
None None
} }
}
} }

View file

@ -3,7 +3,6 @@ use super::MANUAL_FLATTEN;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::visitors::is_local_used; use clippy_utils::visitors::is_local_used;
use clippy_utils::{higher, path_to_local_id, peel_blocks_with_stmt}; use clippy_utils::{higher, path_to_local_id, peel_blocks_with_stmt};
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Expr, Pat, PatKind}; use rustc_hir::{Expr, Pat, PatKind};
@ -21,25 +20,25 @@ pub(super) fn check<'tcx>(
span: Span, span: Span,
) { ) {
let inner_expr = peel_blocks_with_stmt(body); let inner_expr = peel_blocks_with_stmt(body);
if_chain! {
if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None }) if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: None })
= higher::IfLet::hir(cx, inner_expr); = higher::IfLet::hir(cx, inner_expr)
// Ensure match_expr in `if let` statement is the same as the pat from the for-loop // Ensure match_expr in `if let` statement is the same as the pat from the for-loop
if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind; && let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind
if path_to_local_id(let_expr, pat_hir_id); && path_to_local_id(let_expr, pat_hir_id)
// Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result` // Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
if let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind; && let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind
if let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id); && let Res::Def(DefKind::Ctor(..), ctor_id) = cx.qpath_res(qpath, let_pat.hir_id)
if let Some(variant_id) = cx.tcx.opt_parent(ctor_id); && let Some(variant_id) = cx.tcx.opt_parent(ctor_id)
let some_ctor = cx.tcx.lang_items().option_some_variant() == Some(variant_id); && let some_ctor = cx.tcx.lang_items().option_some_variant() == Some(variant_id)
let ok_ctor = cx.tcx.lang_items().result_ok_variant() == Some(variant_id); && let ok_ctor = cx.tcx.lang_items().result_ok_variant() == Some(variant_id)
if some_ctor || ok_ctor; && (some_ctor || ok_ctor)
// Ensure expr in `if let` is not used afterwards // Ensure expr in `if let` is not used afterwards
if !is_local_used(cx, if_then, pat_hir_id); && !is_local_used(cx, if_then, pat_hir_id)
then { {
let if_let_type = if some_ctor { "Some" } else { "Ok" }; let if_let_type = if some_ctor { "Some" } else { "Ok" };
// Prepare the error message // Prepare the error message
let msg = format!("unnecessary `if let` since only the `{if_let_type}` variant of the iterator element is used"); let msg =
format!("unnecessary `if let` since only the `{if_let_type}` variant of the iterator element is used");
// Prepare the help message // Prepare the help message
let mut applicability = Applicability::MaybeIncorrect; let mut applicability = Applicability::MaybeIncorrect;
@ -47,40 +46,25 @@ pub(super) fn check<'tcx>(
let copied = match cx.typeck_results().expr_ty(let_expr).kind() { let copied = match cx.typeck_results().expr_ty(let_expr).kind() {
ty::Ref(_, inner, _) => match inner.kind() { ty::Ref(_, inner, _) => match inner.kind() {
ty::Ref(..) => ".copied()", ty::Ref(..) => ".copied()",
_ => "" _ => "",
} },
_ => "" _ => "",
}; };
let sugg = format!("{arg_snippet}{copied}.flatten()"); let sugg = format!("{arg_snippet}{copied}.flatten()");
// If suggestion is not a one-liner, it won't be shown inline within the error message. In that case, // If suggestion is not a one-liner, it won't be shown inline within the error message. In that
// it will be shown in the extra `help` message at the end, which is why the first `help_msg` needs // case, it will be shown in the extra `help` message at the end, which is why the first
// to refer to the correct relative position of the suggestion. // `help_msg` needs to refer to the correct relative position of the suggestion.
let help_msg = if sugg.contains('\n') { let help_msg = if sugg.contains('\n') {
"remove the `if let` statement in the for loop and then..." "remove the `if let` statement in the for loop and then..."
} else { } else {
"...and remove the `if let` statement in the for loop" "...and remove the `if let` statement in the for loop"
}; };
span_lint_and_then( span_lint_and_then(cx, MANUAL_FLATTEN, span, &msg, |diag| {
cx, diag.span_suggestion(arg.span, "try", sugg, applicability);
MANUAL_FLATTEN, diag.span_help(inner_expr.span, help_msg);
span, });
&msg,
|diag| {
diag.span_suggestion(
arg.span,
"try",
sugg,
applicability,
);
diag.span_help(
inner_expr.span,
help_msg,
);
}
);
}
} }
} }

View file

@ -4,7 +4,6 @@ use clippy_utils::source::snippet;
use clippy_utils::sugg::Sugg; use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_copy; use clippy_utils::ty::is_copy;
use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg}; use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg};
use if_chain::if_chain;
use rustc_ast::ast; use rustc_ast::ast;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::walk_block; use rustc_hir::intravisit::walk_block;
@ -59,23 +58,32 @@ pub(super) fn check<'tcx>(
.map(|o| { .map(|o| {
o.and_then(|(lhs, rhs)| { o.and_then(|(lhs, rhs)| {
let rhs = fetch_cloned_expr(rhs); let rhs = fetch_cloned_expr(rhs);
if_chain! { if let ExprKind::Index(base_left, idx_left, _) = lhs.kind
if let ExprKind::Index(base_left, idx_left, _) = lhs.kind; && let ExprKind::Index(base_right, idx_right, _) = rhs.kind
if let ExprKind::Index(base_right, idx_right, _) = rhs.kind; && let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left))
if let Some(ty) = get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_left)); && get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some()
if get_slice_like_element_ty(cx, cx.typeck_results().expr_ty(base_right)).is_some(); && let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts)
if let Some((start_left, offset_left)) = get_details_from_idx(cx, idx_left, &starts); && let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts)
if let Some((start_right, offset_right)) = get_details_from_idx(cx, idx_right, &starts);
// Source and destination must be different // Source and destination must be different
if path_to_local(base_left) != path_to_local(base_right); && path_to_local(base_left) != path_to_local(base_right)
then { {
Some((ty, IndexExpr { base: base_left, idx: start_left, idx_offset: offset_left }, Some((
IndexExpr { base: base_right, idx: start_right, idx_offset: offset_right })) ty,
IndexExpr {
base: base_left,
idx: start_left,
idx_offset: offset_left,
},
IndexExpr {
base: base_right,
idx: start_right,
idx_offset: offset_right,
},
))
} else { } else {
None None
} }
}
}) })
}) })
.map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src))) .map(|o| o.map(|(ty, dst, src)| build_manual_memcpy_suggestion(cx, start, end, limits, ty, &dst, &src)))
@ -118,11 +126,10 @@ fn build_manual_memcpy_suggestion<'tcx>(
} }
let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| { let print_limit = |end: &Expr<'_>, end_str: &str, base: &Expr<'_>, sugg: MinifyingSugg<'static>| {
if_chain! { if let ExprKind::MethodCall(method, recv, [], _) = end.kind
if let ExprKind::MethodCall(method, recv, [], _) = end.kind; && method.ident.name == sym::len
if method.ident.name == sym::len; && path_to_local(recv) == path_to_local(base)
if path_to_local(recv) == path_to_local(base); {
then {
if sugg.to_string() == end_str { if sugg.to_string() == end_str {
sugg::EMPTY.into() sugg::EMPTY.into()
} else { } else {
@ -130,13 +137,10 @@ fn build_manual_memcpy_suggestion<'tcx>(
} }
} else { } else {
match limits { match limits {
ast::RangeLimits::Closed => { ast::RangeLimits::Closed => sugg + &sugg::ONE.into(),
sugg + &sugg::ONE.into()
},
ast::RangeLimits::HalfOpen => sugg, ast::RangeLimits::HalfOpen => sugg,
} }
} }
}
}; };
let start_str = Sugg::hir(cx, start, "").into(); let start_str = Sugg::hir(cx, start, "").into();
@ -174,7 +178,9 @@ fn build_manual_memcpy_suggestion<'tcx>(
let dst_base_str = snippet(cx, dst.base.span, "???"); let dst_base_str = snippet(cx, dst.base.span, "???");
let src_base_str = snippet(cx, src.base.span, "???"); let src_base_str = snippet(cx, src.base.span, "???");
let dst = if dst_offset == sugg::EMPTY && dst_limit == sugg::EMPTY { let dst = if (dst_offset == sugg::EMPTY && dst_limit == sugg::EMPTY)
|| is_array_length_equal_to_range(cx, start, end, dst.base)
{
dst_base_str dst_base_str
} else { } else {
format!("{dst_base_str}[{}..{}]", dst_offset.maybe_par(), dst_limit.maybe_par()).into() format!("{dst_base_str}[{}..{}]", dst_offset.maybe_par(), dst_limit.maybe_par()).into()
@ -186,11 +192,13 @@ fn build_manual_memcpy_suggestion<'tcx>(
"clone_from_slice" "clone_from_slice"
}; };
format!( let src = if is_array_length_equal_to_range(cx, start, end, src.base) {
"{dst}.{method_str}(&{src_base_str}[{}..{}]);", src_base_str
src_offset.maybe_par(), } else {
src_limit.maybe_par() format!("{src_base_str}[{}..{}]", src_offset.maybe_par(), src_limit.maybe_par()).into()
) };
format!("{dst}.{method_str}(&{src});")
} }
/// a wrapper of `Sugg`. Besides what `Sugg` do, this removes unnecessary `0`; /// a wrapper of `Sugg`. Besides what `Sugg` do, this removes unnecessary `0`;
@ -331,10 +339,12 @@ fn get_slice_like_element_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Opti
} }
fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> { fn fetch_cloned_expr<'tcx>(expr: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
if_chain! { if let ExprKind::MethodCall(method, arg, [], _) = expr.kind
if let ExprKind::MethodCall(method, arg, [], _) = expr.kind; && method.ident.name == sym::clone
if method.ident.name == sym::clone; {
then { arg } else { expr } arg
} else {
expr
} }
} }
@ -446,3 +456,34 @@ fn get_loop_counters<'a, 'tcx>(
.into() .into()
}) })
} }
fn is_array_length_equal_to_range(cx: &LateContext<'_>, start: &Expr<'_>, end: &Expr<'_>, arr: &Expr<'_>) -> bool {
fn extract_lit_value(expr: &Expr<'_>) -> Option<u128> {
if let ExprKind::Lit(lit) = expr.kind
&& let ast::LitKind::Int(value, _) = lit.node
{
Some(value)
} else {
None
}
}
let arr_ty = cx.typeck_results().expr_ty(arr).peel_refs();
if let ty::Array(_, s) = arr_ty.kind() {
let size: u128 = if let Some(size) = s.try_eval_target_usize(cx.tcx, cx.param_env) {
size.into()
} else {
return false;
};
let range = match (extract_lit_value(start), extract_lit_value(end)) {
(Some(start_value), Some(end_value)) => end_value - start_value,
_ => return false,
};
size == range
} else {
false
}
}

View file

@ -31,13 +31,17 @@ fn unpack_cond<'tcx>(cond: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
} }
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &'tcx Expr<'_>) { pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &'tcx Expr<'_>) {
if_chain! { if let ExprKind::Block(
if let ExprKind::Block(Block { stmts: [], expr: None, ..}, _) = body.kind; Block {
if let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind; stmts: [], expr: None, ..
if [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name); },
if let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind(); _,
if cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did()); ) = body.kind
then { && let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind
&& [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name)
&& let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind()
&& cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did())
{
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
MISSING_SPIN_LOOP, MISSING_SPIN_LOOP,
@ -48,9 +52,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &'
"{ core::hint::spin_loop() }" "{ core::hint::spin_loop() }"
} else { } else {
"{ std::hint::spin_loop() }" "{ std::hint::spin_loop() }"
}).into(), })
Applicability::MachineApplicable .into(),
Applicability::MachineApplicable,
); );
} }
}
} }

View file

@ -1,7 +1,6 @@
use super::MUT_RANGE_BOUND; use super::MUT_RANGE_BOUND;
use clippy_utils::diagnostics::span_lint_and_note; use clippy_utils::diagnostics::span_lint_and_note;
use clippy_utils::{get_enclosing_block, higher, path_to_local}; use clippy_utils::{get_enclosing_block, higher, path_to_local};
use if_chain::if_chain;
use rustc_hir::intravisit::{self, Visitor}; use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind}; use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, Node, PatKind};
use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId}; use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
@ -12,20 +11,18 @@ use rustc_middle::ty;
use rustc_span::Span; use rustc_span::Span;
pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) { pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
if_chain! {
if let Some(higher::Range { if let Some(higher::Range {
start: Some(start), start: Some(start),
end: Some(end), end: Some(end),
.. ..
}) = higher::Range::hir(arg); }) = higher::Range::hir(arg)
let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end)); && let (mut_id_start, mut_id_end) = (check_for_mutability(cx, start), check_for_mutability(cx, end))
if mut_id_start.is_some() || mut_id_end.is_some(); && (mut_id_start.is_some() || mut_id_end.is_some())
then { {
let (span_low, span_high) = check_for_mutation(cx, body, mut_id_start, mut_id_end); let (span_low, span_high) = check_for_mutation(cx, body, mut_id_start, mut_id_end);
mut_warn_with_span(cx, span_low); mut_warn_with_span(cx, span_low);
mut_warn_with_span(cx, span_high); mut_warn_with_span(cx, span_high);
} }
}
} }
fn mut_warn_with_span(cx: &LateContext<'_>, span: Option<Span>) { fn mut_warn_with_span(cx: &LateContext<'_>, span: Option<Span>) {
@ -42,14 +39,12 @@ fn mut_warn_with_span(cx: &LateContext<'_>, span: Option<Span>) {
} }
fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId> { fn check_for_mutability(cx: &LateContext<'_>, bound: &Expr<'_>) -> Option<HirId> {
if_chain! { if let Some(hir_id) = path_to_local(bound)
if let Some(hir_id) = path_to_local(bound); && let Node::Pat(pat) = cx.tcx.hir().get(hir_id)
if let Node::Pat(pat) = cx.tcx.hir().get(hir_id); && let PatKind::Binding(BindingAnnotation::MUT, ..) = pat.kind
if let PatKind::Binding(BindingAnnotation::MUT, ..) = pat.kind; {
then {
return Some(hir_id); return Some(hir_id);
} }
}
None None
} }

View file

@ -4,7 +4,6 @@ use clippy_utils::source::snippet;
use clippy_utils::ty::has_iter_method; use clippy_utils::ty::has_iter_method;
use clippy_utils::visitors::is_local_used; use clippy_utils::visitors::is_local_used;
use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq}; use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq};
use if_chain::if_chain;
use rustc_ast::ast; use rustc_ast::ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
@ -187,16 +186,14 @@ pub(super) fn check<'tcx>(
} }
fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool { fn is_len_call(expr: &Expr<'_>, var: Symbol) -> bool {
if_chain! { if let ExprKind::MethodCall(method, recv, [], _) = expr.kind
if let ExprKind::MethodCall(method, recv, [], _) = expr.kind; && method.ident.name == sym::len
if method.ident.name == sym::len; && let ExprKind::Path(QPath::Resolved(_, path)) = recv.kind
if let ExprKind::Path(QPath::Resolved(_, path)) = recv.kind; && path.segments.len() == 1
if path.segments.len() == 1; && path.segments[0].ident.name == var
if path.segments[0].ident.name == var; {
then {
return true; return true;
} }
}
false false
} }
@ -207,18 +204,16 @@ fn is_end_eq_array_len<'tcx>(
limits: ast::RangeLimits, limits: ast::RangeLimits,
indexed_ty: Ty<'tcx>, indexed_ty: Ty<'tcx>,
) -> bool { ) -> bool {
if_chain! { if let ExprKind::Lit(lit) = end.kind
if let ExprKind::Lit(lit) = end.kind; && let ast::LitKind::Int(end_int, _) = lit.node
if let ast::LitKind::Int(end_int, _) = lit.node; && let ty::Array(_, arr_len_const) = indexed_ty.kind()
if let ty::Array(_, arr_len_const) = indexed_ty.kind(); && let Some(arr_len) = arr_len_const.try_eval_target_usize(cx.tcx, cx.param_env)
if let Some(arr_len) = arr_len_const.try_eval_target_usize(cx.tcx, cx.param_env); {
then {
return match limits { return match limits {
ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(), ast::RangeLimits::Closed => end_int + 1 >= arr_len.into(),
ast::RangeLimits::HalfOpen => end_int >= arr_len.into(), ast::RangeLimits::HalfOpen => end_int >= arr_len.into(),
}; };
} }
}
false false
} }
@ -248,13 +243,12 @@ struct VarVisitor<'a, 'tcx> {
impl<'a, 'tcx> VarVisitor<'a, 'tcx> { impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool { fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
if_chain! { if let ExprKind::Path(ref seqpath) = seqexpr.kind
// the indexed container is referenced by a name // the indexed container is referenced by a name
if let ExprKind::Path(ref seqpath) = seqexpr.kind; && let QPath::Resolved(None, seqvar) = *seqpath
if let QPath::Resolved(None, seqvar) = *seqpath; && seqvar.segments.len() == 1
if seqvar.segments.len() == 1; && is_local_used(self.cx, idx, self.var)
if is_local_used(self.cx, idx, self.var); {
then {
if self.prefer_mutable { if self.prefer_mutable {
self.indexed_mut.insert(seqvar.segments[0].ident.name); self.indexed_mut.insert(seqvar.segments[0].ident.name);
} }
@ -294,43 +288,37 @@ impl<'a, 'tcx> VarVisitor<'a, 'tcx> {
_ => (), _ => (),
} }
} }
}
true true
} }
} }
impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) { fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
if_chain! { if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind
// a range index op // a range index op
if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind; && let Some(trait_id) = self
if let Some(trait_id) = self
.cx .cx
.typeck_results() .typeck_results()
.type_dependent_def_id(expr.hir_id) .type_dependent_def_id(expr.hir_id)
.and_then(|def_id| self.cx.tcx.trait_of_item(def_id)); .and_then(|def_id| self.cx.tcx.trait_of_item(def_id))
if (meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id)) && ((meth.ident.name == sym::index && self.cx.tcx.lang_items().index_trait() == Some(trait_id))
|| (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id)); || (meth.ident.name == sym::index_mut && self.cx.tcx.lang_items().index_mut_trait() == Some(trait_id)))
if !self.check(args_1, args_0, expr); && !self.check(args_1, args_0, expr)
then { {
return; return;
} }
}
if_chain! { if let ExprKind::Index(seqexpr, idx, _) = expr.kind
// an index op // an index op
if let ExprKind::Index(seqexpr, idx, _) = expr.kind; && !self.check(idx, seqexpr, expr)
if !self.check(idx, seqexpr, expr); {
then {
return; return;
} }
}
if_chain! { if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind
// directly using a variable // directly using a variable
if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind; && let Res::Local(local_id) = path.res
if let Res::Local(local_id) = path.res; {
then {
if local_id == self.var { if local_id == self.var {
self.nonindex = true; self.nonindex = true;
} else { } else {
@ -338,7 +326,6 @@ impl<'a, 'tcx> Visitor<'tcx> for VarVisitor<'a, 'tcx> {
self.referenced.insert(path.segments[0].ident.name); self.referenced.insert(path.segments[0].ident.name);
} }
} }
}
let old = self.prefer_mutable; let old = self.prefer_mutable;
match expr.kind { match expr.kind {

View file

@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::path_to_local; use clippy_utils::path_to_local;
use clippy_utils::source::snippet_with_context; use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item}; use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
use if_chain::if_chain;
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
@ -44,17 +43,16 @@ pub(super) fn check<'tcx>(
// Determine whether it is safe to lint the body // Determine whether it is safe to lint the body
let mut same_item_push_visitor = SameItemPushVisitor::new(cx); let mut same_item_push_visitor = SameItemPushVisitor::new(cx);
walk_expr(&mut same_item_push_visitor, body); walk_expr(&mut same_item_push_visitor, body);
if_chain! { if same_item_push_visitor.should_lint()
if same_item_push_visitor.should_lint(); && let Some((vec, pushed_item, ctxt)) = same_item_push_visitor.vec_push
if let Some((vec, pushed_item, ctxt)) = same_item_push_visitor.vec_push; && let vec_ty = cx.typeck_results().expr_ty(vec)
let vec_ty = cx.typeck_results().expr_ty(vec); && let ty = vec_ty.walk().nth(1).unwrap().expect_ty()
let ty = vec_ty.walk().nth(1).unwrap().expect_ty(); && cx
if cx
.tcx .tcx
.lang_items() .lang_items()
.clone_trait() .clone_trait()
.map_or(false, |id| implements_trait(cx, ty, id, &[])); .map_or(false, |id| implements_trait(cx, ty, id, &[]))
then { {
// Make sure that the push does not involve possibly mutating values // Make sure that the push does not involve possibly mutating values
match pushed_item.kind { match pushed_item.kind {
ExprKind::Path(ref qpath) => { ExprKind::Path(ref qpath) => {
@ -62,14 +60,13 @@ pub(super) fn check<'tcx>(
// immutable bindings that are initialized with literal or constant // immutable bindings that are initialized with literal or constant
Res::Local(hir_id) => { Res::Local(hir_id) => {
let node = cx.tcx.hir().get(hir_id); let node = cx.tcx.hir().get(hir_id);
if_chain! { if let Node::Pat(pat) = node
if let Node::Pat(pat) = node; && let PatKind::Binding(bind_ann, ..) = pat.kind
if let PatKind::Binding(bind_ann, ..) = pat.kind; && !matches!(bind_ann, BindingAnnotation(_, Mutability::Mut))
if !matches!(bind_ann, BindingAnnotation(_, Mutability::Mut)); && let parent_node = cx.tcx.hir().parent_id(hir_id)
let parent_node = cx.tcx.hir().parent_id(hir_id); && let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node)
if let Some(Node::Local(parent_let_expr)) = cx.tcx.hir().find(parent_node); && let Some(init) = parent_let_expr.init
if let Some(init) = parent_let_expr.init; {
then {
match init.kind { match init.kind {
// immutable bindings that are initialized with literal // immutable bindings that are initialized with literal
ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt), ExprKind::Lit(..) => emit_lint(cx, vec, pushed_item, ctxt),
@ -78,11 +75,10 @@ pub(super) fn check<'tcx>(
if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) { if let Res::Def(DefKind::Const, ..) = cx.qpath_res(path, init.hir_id) {
emit_lint(cx, vec, pushed_item, ctxt); emit_lint(cx, vec, pushed_item, ctxt);
} }
} },
_ => {}, _ => {},
} }
} }
}
}, },
// constant // constant
Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt), Res::Def(DefKind::Const, ..) => emit_lint(cx, vec, pushed_item, ctxt),
@ -93,7 +89,6 @@ pub(super) fn check<'tcx>(
_ => {}, _ => {},
} }
} }
}
} }
// Scans the body of the for loop and determines whether lint should be given // Scans the body of the for loop and determines whether lint should be given
@ -118,18 +113,16 @@ impl<'a, 'tcx> SameItemPushVisitor<'a, 'tcx> {
} }
fn should_lint(&self) -> bool { fn should_lint(&self) -> bool {
if_chain! { if !self.non_deterministic_expr
if !self.non_deterministic_expr; && !self.multiple_pushes
if !self.multiple_pushes; && let Some((vec, _, _)) = self.vec_push
if let Some((vec, _, _)) = self.vec_push; && let Some(hir_id) = path_to_local(vec)
if let Some(hir_id) = path_to_local(vec); {
then {
!self.used_locals.contains(&hir_id) !self.used_locals.contains(&hir_id)
} else { } else {
false false
} }
} }
}
} }
impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for SameItemPushVisitor<'a, 'tcx> {
@ -180,18 +173,16 @@ fn get_vec_push<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
stmt: &'tcx Stmt<'_>, stmt: &'tcx Stmt<'_>,
) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, SyntaxContext)> { ) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, SyntaxContext)> {
if_chain! { if let StmtKind::Semi(semi_stmt) = &stmt.kind
// Extract method being called // Extract method being called
if let StmtKind::Semi(semi_stmt) = &stmt.kind; && let ExprKind::MethodCall(path, self_expr, args, _) = &semi_stmt.kind
if let ExprKind::MethodCall(path, self_expr, args, _) = &semi_stmt.kind;
// Figure out the parameters for the method call // Figure out the parameters for the method call
if let Some(pushed_item) = args.first(); && let Some(pushed_item) = args.first()
// Check that the method being called is push() on a Vec // Check that the method being called is push() on a Vec
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec); && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_expr), sym::Vec)
if path.ident.name.as_str() == "push"; && path.ident.name.as_str() == "push"
then { {
return Some((self_expr, pushed_item, semi_stmt.span.ctxt())) return Some((self_expr, pushed_item, semi_stmt.span.ctxt()));
}
} }
None None
} }

View file

@ -2,7 +2,6 @@ use super::SINGLE_ELEMENT_LOOP;
use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::{indent_of, snippet_with_applicability}; use clippy_utils::source::{indent_of, snippet_with_applicability};
use clippy_utils::visitors::contains_break_or_continue; use clippy_utils::visitors::contains_break_or_continue;
use if_chain::if_chain;
use rustc_ast::util::parser::PREC_PREFIX; use rustc_ast::util::parser::PREC_PREFIX;
use rustc_ast::Mutability; use rustc_ast::Mutability;
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -66,11 +65,10 @@ pub(super) fn check<'tcx>(
ExprKind::Array([arg]) if cx.tcx.sess.edition() >= Edition::Edition2021 => (arg, ""), ExprKind::Array([arg]) if cx.tcx.sess.edition() >= Edition::Edition2021 => (arg, ""),
_ => return, _ => return,
}; };
if_chain! { if let ExprKind::Block(block, _) = body.kind
if let ExprKind::Block(block, _) = body.kind; && !block.stmts.is_empty()
if !block.stmts.is_empty(); && !contains_break_or_continue(body)
if !contains_break_or_continue(body); {
then {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability); let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability);
let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability); let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);
@ -80,10 +78,12 @@ pub(super) fn check<'tcx>(
let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)); let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0));
// Reference iterator from `&(mut) []` or `[].iter(_mut)()`. // Reference iterator from `&(mut) []` or `[].iter(_mut)()`.
if !prefix.is_empty() && ( if !prefix.is_empty()
&& (
// Precedence of internal expression is less than or equal to precedence of `&expr`. // Precedence of internal expression is less than or equal to precedence of `&expr`.
arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression) arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression)
) { )
{
arg_snip = format!("({arg_snip})").into(); arg_snip = format!("({arg_snip})").into();
} }
@ -95,7 +95,6 @@ pub(super) fn check<'tcx>(
"try", "try",
format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"), format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"),
applicability, applicability,
) );
}
} }
} }

View file

@ -9,7 +9,7 @@ use rustc_middle::ty;
/// Checks for the `UNUSED_ENUMERATE_INDEX` lint. /// Checks for the `UNUSED_ENUMERATE_INDEX` lint.
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) { pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx Expr<'_>, body: &'tcx Expr<'_>) {
let PatKind::Tuple(tuple, _) = pat.kind else { let PatKind::Tuple([index, elem], _) = pat.kind else {
return; return;
}; };
@ -19,7 +19,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx
let ty = cx.typeck_results().expr_ty(arg); let ty = cx.typeck_results().expr_ty(arg);
if !pat_is_wild(cx, &tuple[0].kind, body) { if !pat_is_wild(cx, &index.kind, body) {
return; return;
} }
@ -53,7 +53,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx
diag, diag,
"remove the `.enumerate()` call", "remove the `.enumerate()` call",
vec![ vec![
(pat.span, snippet(cx, tuple[1].span, "..").into_owned()), (pat.span, snippet(cx, elem.span, "..").into_owned()),
(arg.span, base_iter.to_string()), (arg.span, base_iter.to_string()),
], ],
); );

View file

@ -1,6 +1,5 @@
use clippy_utils::ty::{has_iter_method, implements_trait}; use clippy_utils::ty::{has_iter_method, implements_trait};
use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg}; use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg};
use if_chain::if_chain;
use rustc_ast::ast::{LitIntType, LitKind}; use rustc_ast::ast::{LitIntType, LitKind};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, Visitor}; use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, Visitor};
@ -145,10 +144,9 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
fn visit_local(&mut self, l: &'tcx Local<'_>) { fn visit_local(&mut self, l: &'tcx Local<'_>) {
// Look for declarations of the variable // Look for declarations of the variable
if_chain! { if l.pat.hir_id == self.var_id
if l.pat.hir_id == self.var_id; && let PatKind::Binding(.., ident, _) = l.pat.kind
if let PatKind::Binding(.., ident, _) = l.pat.kind; {
then {
let ty = l.ty.map(|_| self.cx.typeck_results().pat_ty(l.pat)); let ty = l.ty.map(|_| self.cx.typeck_results().pat_ty(l.pat));
self.state = l.init.map_or(InitializeVisitorState::Declared(ident.name, ty), |init| { self.state = l.init.map_or(InitializeVisitorState::Declared(ident.name, ty), |init| {
@ -157,8 +155,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
ty, ty,
name: ident.name, name: ident.name,
} }
}) });
}
} }
walk_local(self, l); walk_local(self, l);

View file

@ -2,7 +2,6 @@ use super::WHILE_IMMUTABLE_CONDITION;
use clippy_utils::consts::constant; use clippy_utils::consts::constant;
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::usage::mutated_variables; use clippy_utils::usage::mutated_variables;
use if_chain::if_chain;
use rustc_hir::def::{DefKind, Res}; use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefIdMap; use rustc_hir::def_id::DefIdMap;
use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::intravisit::{walk_expr, Visitor};
@ -95,10 +94,9 @@ struct VarCollectorVisitor<'a, 'tcx> {
impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> { impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> {
fn insert_def_id(&mut self, ex: &'tcx Expr<'_>) { fn insert_def_id(&mut self, ex: &'tcx Expr<'_>) {
if_chain! { if let ExprKind::Path(ref qpath) = ex.kind
if let ExprKind::Path(ref qpath) = ex.kind; && let QPath::Resolved(None, _) = *qpath
if let QPath::Resolved(None, _) = *qpath; {
then {
match self.cx.qpath_res(qpath, ex.hir_id) { match self.cx.qpath_res(qpath, ex.hir_id) {
Res::Local(hir_id) => { Res::Local(hir_id) => {
self.ids.insert(hir_id); self.ids.insert(hir_id);
@ -111,7 +109,6 @@ impl<'a, 'tcx> VarCollectorVisitor<'a, 'tcx> {
} }
} }
} }
}
} }
impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for VarCollectorVisitor<'a, 'tcx> {

View file

@ -3,7 +3,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_applicability; use clippy_utils::source::snippet_with_applicability;
use clippy_utils::visitors::is_res_used; use clippy_utils::visitors::is_res_used;
use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_res_lang_ctor, is_trait_method}; use clippy_utils::{get_enclosing_loop_or_multi_call_closure, higher, is_refutable, is_res_lang_ctor, is_trait_method};
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::def::Res; use rustc_hir::def::Res;
use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::intravisit::{walk_expr, Visitor};
@ -15,25 +14,18 @@ use rustc_span::symbol::sym;
use rustc_span::Symbol; use rustc_span::Symbol;
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let (scrutinee_expr, iter_expr_struct, iter_expr, some_pat, loop_expr) = if_chain! { if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr)
if let Some(higher::WhileLet { if_then, let_pat, let_expr }) = higher::WhileLet::hir(expr);
// check for `Some(..)` pattern // check for `Some(..)` pattern
if let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind; && let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind
if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome); && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome)
// check for call to `Iterator::next` // check for call to `Iterator::next`
if let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind; && let ExprKind::MethodCall(method_name, iter_expr, [], _) = let_expr.kind
if method_name.ident.name == sym::next; && method_name.ident.name == sym::next
if is_trait_method(cx, let_expr, sym::Iterator); && is_trait_method(cx, let_expr, sym::Iterator)
if let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr); && let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr)
// get the loop containing the match expression // get the loop containing the match expression
if !uses_iter(cx, &iter_expr_struct, if_then); && !uses_iter(cx, &iter_expr_struct, if_then)
then { {
(let_expr, iter_expr_struct, iter_expr, some_pat, expr)
} else {
return;
}
};
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let loop_var = if let Some(some_pat) = some_pat.first() { let loop_var = if let Some(some_pat) = some_pat.first() {
if is_refutable(cx, some_pat) { if is_refutable(cx, some_pat) {
@ -51,7 +43,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut) let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut)
|| !iter_expr_struct.can_move || !iter_expr_struct.can_move
|| !iter_expr_struct.fields.is_empty() || !iter_expr_struct.fields.is_empty()
|| needs_mutable_borrow(cx, &iter_expr_struct, loop_expr) || needs_mutable_borrow(cx, &iter_expr_struct, expr)
{ {
".by_ref()" ".by_ref()"
} else { } else {
@ -62,12 +54,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
WHILE_LET_ON_ITERATOR, WHILE_LET_ON_ITERATOR,
expr.span.with_hi(scrutinee_expr.span.hi()), expr.span.with_hi(let_expr.span.hi()),
"this loop could be written as a `for` loop", "this loop could be written as a `for` loop",
"try", "try",
format!("for {loop_var} in {iterator}{by_ref}"), format!("for {loop_var} in {iterator}{by_ref}"),
applicability, applicability,
); );
}
} }
#[derive(Debug)] #[derive(Debug)]

View file

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::diagnostics::span_lint_hir_and_then;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use hir::def::{DefKind, Res}; use hir::def::{DefKind, Res};
use if_chain::if_chain;
use rustc_ast::ast; use rustc_ast::ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability; use rustc_errors::Applicability;
@ -89,18 +88,17 @@ impl MacroUseImports {
impl<'tcx> LateLintPass<'tcx> for MacroUseImports { impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
if_chain! { if cx.sess().opts.edition >= Edition::Edition2018
if cx.sess().opts.edition >= Edition::Edition2018; && let hir::ItemKind::Use(path, _kind) = &item.kind
if let hir::ItemKind::Use(path, _kind) = &item.kind; && let hir_id = item.hir_id()
let hir_id = item.hir_id(); && let attrs = cx.tcx.hir().attrs(hir_id)
let attrs = cx.tcx.hir().attrs(hir_id); && let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use))
if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use)); && let Some(id) = path.res.iter().find_map(|res| match res {
if let Some(id) = path.res.iter().find_map(|res| match res {
Res::Def(DefKind::Mod, id) => Some(id), Res::Def(DefKind::Mod, id) => Some(id),
_ => None, _ => None,
}); })
if !id.is_local(); && !id.is_local()
then { {
for kid in cx.tcx.module_children(id) { for kid in cx.tcx.module_children(id) {
if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res { if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
let span = mac_attr.span; let span = mac_attr.span;
@ -108,13 +106,10 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
self.imports.push((def_path, span, hir_id)); self.imports.push((def_path, span, hir_id));
} }
} }
} else { } else if item.span.from_expansion() {
if item.span.from_expansion() {
self.push_unique_macro_pat_ty(cx, item.span); self.push_unique_macro_pat_ty(cx, item.span);
} }
} }
}
}
fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) { fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &ast::Attribute) {
if attr.span.from_expansion() { if attr.span.from_expansion() {
self.push_unique_macro(cx, attr.span); self.push_unique_macro(cx, attr.span);

View file

@ -1,7 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::{is_entrypoint_fn, is_no_std_crate}; use clippy_utils::{is_entrypoint_fn, is_no_std_crate};
use if_chain::if_chain;
use rustc_hir::{Expr, ExprKind, QPath}; use rustc_hir::{Expr, ExprKind, QPath};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass}; use rustc_session::{declare_tool_lint, impl_lint_pass};
@ -43,21 +42,19 @@ impl LateLintPass<'_> for MainRecursion {
return; return;
} }
if_chain! { if let ExprKind::Call(func, _) = &expr.kind
if let ExprKind::Call(func, _) = &expr.kind; && let ExprKind::Path(QPath::Resolved(_, path)) = &func.kind
if let ExprKind::Path(QPath::Resolved(_, path)) = &func.kind; && let Some(def_id) = path.res.opt_def_id()
if let Some(def_id) = path.res.opt_def_id(); && is_entrypoint_fn(cx, def_id)
if is_entrypoint_fn(cx, def_id); {
then {
span_lint_and_help( span_lint_and_help(
cx, cx,
MAIN_RECURSION, MAIN_RECURSION,
func.span, func.span,
&format!("recursing into entrypoint `{}`", snippet(cx, func.span, "main")), &format!("recursing into entrypoint `{}`", snippet(cx, func.span, "main")),
None, None,
"consider using another function for this recursion" "consider using another function for this recursion",
) );
}
} }
} }
} }

View file

@ -1,6 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt}; use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt};
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::FnKind; use rustc_hir::intravisit::FnKind;
use rustc_hir::{ use rustc_hir::{
@ -47,21 +46,20 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
span: Span, span: Span,
def_id: LocalDefId, def_id: LocalDefId,
) { ) {
if_chain! { if let Some(header) = kind.header()
if let Some(header) = kind.header(); && !header.asyncness.is_async()
if !header.asyncness.is_async();
// Check that this function returns `impl Future` // Check that this function returns `impl Future`
if let FnRetTy::Return(ret_ty) = decl.output; && let FnRetTy::Return(ret_ty) = decl.output
if let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty); && let Some((trait_ref, output_lifetimes)) = future_trait_ref(cx, ret_ty)
if let Some(output) = future_output_ty(trait_ref); && let Some(output) = future_output_ty(trait_ref)
if captures_all_lifetimes(decl.inputs, &output_lifetimes); && captures_all_lifetimes(decl.inputs, &output_lifetimes)
// Check that the body of the function consists of one async block // Check that the body of the function consists of one async block
if let ExprKind::Block(block, _) = body.value.kind; && let ExprKind::Block(block, _) = body.value.kind
if block.stmts.is_empty(); && block.stmts.is_empty()
if let Some(closure_body) = desugared_async_block(cx, block); && let Some(closure_body) = desugared_async_block(cx, block)
if let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) = && let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) =
cx.tcx.hir().get_by_def_id(def_id); cx.tcx.hir().get_by_def_id(def_id)
then { {
let header_span = span.with_hi(ret_ty.span.hi()); let header_span = span.with_hi(ret_ty.span.hi());
span_lint_and_then( span_lint_and_then(
@ -70,12 +68,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
header_span, header_span,
"this function can be simplified using the `async fn` syntax", "this function can be simplified using the `async fn` syntax",
|diag| { |diag| {
if_chain! { if let Some(vis_snip) = snippet_opt(cx, *vis_span)
if let Some(vis_snip) = snippet_opt(cx, *vis_span); && let Some(header_snip) = snippet_opt(cx, header_span)
if let Some(header_snip) = snippet_opt(cx, header_span); && let Some(ret_pos) = position_before_rarrow(&header_snip)
if let Some(ret_pos) = position_before_rarrow(&header_snip); && let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output)
if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output); {
then {
let header_snip = if vis_snip.is_empty() { let header_snip = if vis_snip.is_empty() {
format!("async {}", &header_snip[..ret_pos]) format!("async {}", &header_snip[..ret_pos])
} else { } else {
@ -87,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
header_span, header_span,
help, help,
format!("{header_snip}{ret_snip}"), format!("{header_snip}{ret_snip}"),
Applicability::MachineApplicable Applicability::MachineApplicable,
); );
let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span)); let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span));
@ -95,34 +92,31 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
block.span, block.span,
"move the body of the async block to the enclosing function", "move the body of the async block to the enclosing function",
body_snip, body_snip,
Applicability::MachineApplicable Applicability::MachineApplicable,
); );
} }
}
}, },
); );
} }
} }
}
} }
fn future_trait_ref<'tcx>( fn future_trait_ref<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
ty: &'tcx Ty<'tcx>, ty: &'tcx Ty<'tcx>,
) -> Option<(&'tcx TraitRef<'tcx>, Vec<LifetimeName>)> { ) -> Option<(&'tcx TraitRef<'tcx>, Vec<LifetimeName>)> {
if_chain! { if let TyKind::OpaqueDef(item_id, bounds, false) = ty.kind
if let TyKind::OpaqueDef(item_id, bounds, false) = ty.kind; && let item = cx.tcx.hir().item(item_id)
let item = cx.tcx.hir().item(item_id); && let ItemKind::OpaqueTy(opaque) = &item.kind
if let ItemKind::OpaqueTy(opaque) = &item.kind; && let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| {
if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| {
if let GenericBound::Trait(poly, _) = bound { if let GenericBound::Trait(poly, _) = bound {
Some(&poly.trait_ref) Some(&poly.trait_ref)
} else { } else {
None None
} }
}); })
if trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait(); && trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait()
then { {
let output_lifetimes = bounds let output_lifetimes = bounds
.iter() .iter()
.filter_map(|bound| { .filter_map(|bound| {
@ -136,23 +130,20 @@ fn future_trait_ref<'tcx>(
return Some((trait_ref, output_lifetimes)); return Some((trait_ref, output_lifetimes));
} }
}
None None
} }
fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'tcx>> { fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'tcx>> {
if_chain! { if let Some(segment) = trait_ref.path.segments.last()
if let Some(segment) = trait_ref.path.segments.last(); && let Some(args) = segment.args
if let Some(args) = segment.args; && args.bindings.len() == 1
if args.bindings.len() == 1; && let binding = &args.bindings[0]
let binding = &args.bindings[0]; && binding.ident.name == sym::Output
if binding.ident.name == sym::Output; && let TypeBindingKind::Equality { term: Term::Ty(output) } = binding.kind
if let TypeBindingKind::Equality { term: Term::Ty(output) } = binding.kind; {
then {
return Some(output); return Some(output);
} }
}
None None
} }
@ -181,18 +172,16 @@ fn captures_all_lifetimes(inputs: &[Ty<'_>], output_lifetimes: &[LifetimeName])
} }
fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> { fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
if_chain! { if let Some(block_expr) = block.expr
if let Some(block_expr) = block.expr; && let Expr {
if let Expr {
kind: ExprKind::Closure(&Closure { body, .. }), kind: ExprKind::Closure(&Closure { body, .. }),
.. ..
} = block_expr; } = block_expr
let closure_body = cx.tcx.hir().body(body); && let closure_body = cx.tcx.hir().body(body)
if closure_body.coroutine_kind == Some(CoroutineKind::Async(CoroutineSource::Block)); && closure_body.coroutine_kind == Some(CoroutineKind::Async(CoroutineSource::Block))
then { {
return Some(closure_body); return Some(closure_body);
} }
}
None None
} }

View file

@ -53,18 +53,17 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
return; return;
} }
if_chain! { if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind
if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind; && let BinOpKind::Mul = &bin_op.node
if let BinOpKind::Mul = &bin_op.node; && !in_external_macro(cx.sess(), expr.span)
if !in_external_macro(cx.sess(), expr.span); && let ctxt = expr.span.ctxt()
let ctxt = expr.span.ctxt(); && left_expr.span.ctxt() == ctxt
if left_expr.span.ctxt() == ctxt; && right_expr.span.ctxt() == ctxt
if right_expr.span.ctxt() == ctxt; && let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr)
if let Some((real_ty, resolved_ty, other_expr)) = get_one_size_of_ty(cx, left_expr, right_expr); && matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_))
if matches!(resolved_ty.kind(), ty::Int(_) | ty::Uint(_)); && let ExprKind::Lit(lit) = &other_expr.kind
if let ExprKind::Lit(lit) = &other_expr.kind; && let LitKind::Int(8, _) = lit.node
if let LitKind::Int(8, _) = lit.node; {
then {
let mut app = Applicability::MachineApplicable; let mut app = Applicability::MachineApplicable;
let ty_snip = snippet_with_context(cx, real_ty.span, ctxt, "..", &mut app).0; let ty_snip = snippet_with_context(cx, real_ty.span, ctxt, "..", &mut app).0;
let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS")); let sugg = create_sugg(cx, expr, format!("{ty_snip}::BITS"));
@ -80,7 +79,6 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
); );
} }
} }
}
extract_msrv_attr!(LateContext); extract_msrv_attr!(LateContext);
} }
@ -98,23 +96,23 @@ fn get_one_size_of_ty<'tcx>(
} }
fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> { fn get_size_of_ty<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<(&'tcx rustc_hir::Ty<'tcx>, Ty<'tcx>)> {
if_chain! { if let ExprKind::Call(count_func, _func_args) = expr.kind
if let ExprKind::Call(count_func, _func_args) = expr.kind; && let ExprKind::Path(ref count_func_qpath) = count_func.kind
if let ExprKind::Path(ref count_func_qpath) = count_func.kind; && let QPath::Resolved(_, count_func_path) = count_func_qpath
&& let Some(segment_zero) = count_func_path.segments.first()
if let QPath::Resolved(_, count_func_path) = count_func_qpath; && let Some(args) = segment_zero.args
if let Some(segment_zero) = count_func_path.segments.first(); && let Some(GenericArg::Type(real_ty)) = args.args.first()
if let Some(args) = segment_zero.args; && let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id()
if let Some(GenericArg::Type(real_ty)) = args.args.first(); && cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id)
{
if let Some(def_id) = cx.qpath_res(count_func_qpath, count_func.hir_id).opt_def_id(); cx.typeck_results()
if cx.tcx.is_diagnostic_item(sym::mem_size_of, def_id); .node_args(count_func.hir_id)
then { .types()
cx.typeck_results().node_args(count_func.hir_id).types().next().map(|resolved_ty| (*real_ty, resolved_ty)) .next()
.map(|resolved_ty| (*real_ty, resolved_ty))
} else { } else {
None None
} }
}
} }
fn create_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, base_sugg: String) -> String { fn create_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, base_sugg: String) -> String {

View file

@ -5,18 +5,15 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::higher::IfLetOrMatch; use clippy_utils::higher::IfLetOrMatch;
use clippy_utils::source::snippet_with_context; use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::visitors::{Descend, Visitable}; use clippy_utils::{is_lint_allowed, is_never_expr, pat_and_expr_can_be_question_mark, peel_blocks};
use clippy_utils::{is_lint_allowed, pat_and_expr_can_be_question_mark, peel_blocks};
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind};
use rustc_hir::{Expr, ExprKind, HirId, ItemId, Local, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind, Ty};
use rustc_lint::{LateContext, LintContext}; use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro; use rustc_middle::lint::in_external_macro;
use rustc_session::declare_tool_lint; use rustc_session::declare_tool_lint;
use rustc_span::symbol::{sym, Symbol}; use rustc_span::symbol::{sym, Symbol};
use rustc_span::Span; use rustc_span::Span;
use std::ops::ControlFlow;
use std::slice; use std::slice;
declare_clippy_lint! { declare_clippy_lint! {
@ -51,7 +48,7 @@ declare_clippy_lint! {
} }
impl<'tcx> QuestionMark { impl<'tcx> QuestionMark {
pub(crate) fn check_manual_let_else(&mut self, cx: &LateContext<'_>, stmt: &'tcx Stmt<'tcx>) { pub(crate) fn check_manual_let_else(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) {
if !self.msrv.meets(msrvs::LET_ELSE) || in_external_macro(cx.sess(), stmt.span) { if !self.msrv.meets(msrvs::LET_ELSE) || in_external_macro(cx.sess(), stmt.span) {
return; return;
} }
@ -67,7 +64,7 @@ impl<'tcx> QuestionMark {
IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => { IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else) => {
if let Some(ident_map) = expr_simple_identity_map(local.pat, let_pat, if_then) if let Some(ident_map) = expr_simple_identity_map(local.pat, let_pat, if_then)
&& let Some(if_else) = if_else && let Some(if_else) = if_else
&& expr_diverges(cx, if_else) && is_never_expr(cx, if_else).is_some()
&& let qm_allowed = is_lint_allowed(cx, QUESTION_MARK, stmt.hir_id) && let qm_allowed = is_lint_allowed(cx, QUESTION_MARK, stmt.hir_id)
&& (qm_allowed || pat_and_expr_can_be_question_mark(cx, let_pat, if_else).is_none()) && (qm_allowed || pat_and_expr_can_be_question_mark(cx, let_pat, if_else).is_none())
{ {
@ -91,10 +88,9 @@ impl<'tcx> QuestionMark {
return; return;
} }
let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes; let check_types = self.matches_behaviour == MatchLintBehaviour::WellKnownTypes;
let diverging_arm_opt = arms let diverging_arm_opt = arms.iter().enumerate().find(|(_, arm)| {
.iter() is_never_expr(cx, arm.body).is_some() && pat_allowed_for_else(cx, arm.pat, check_types)
.enumerate() });
.find(|(_, arm)| expr_diverges(cx, arm.body) && pat_allowed_for_else(cx, arm.pat, check_types));
let Some((idx, diverging_arm)) = diverging_arm_opt else { let Some((idx, diverging_arm)) = diverging_arm_opt else {
return; return;
}; };
@ -272,104 +268,6 @@ fn replace_in_pattern(
sn_pat.into_owned() sn_pat.into_owned()
} }
/// Check whether an expression is divergent. May give false negatives.
fn expr_diverges(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
struct V<'cx, 'tcx> {
cx: &'cx LateContext<'tcx>,
res: ControlFlow<(), Descend>,
}
impl<'tcx> Visitor<'tcx> for V<'_, '_> {
fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
fn is_never(cx: &LateContext<'_>, expr: &'_ Expr<'_>) -> bool {
if let Some(ty) = cx.typeck_results().expr_ty_opt(expr) {
return ty.is_never();
}
false
}
if self.res.is_break() {
return;
}
// We can't just call is_never on expr and be done, because the type system
// sometimes coerces the ! type to something different before we can get
// our hands on it. So instead, we do a manual search. We do fall back to
// is_never in some places when there is no better alternative.
self.res = match e.kind {
ExprKind::Continue(_) | ExprKind::Break(_, _) | ExprKind::Ret(_) => ControlFlow::Break(()),
ExprKind::Call(call, _) => {
if is_never(self.cx, e) || is_never(self.cx, call) {
ControlFlow::Break(())
} else {
ControlFlow::Continue(Descend::Yes)
}
},
ExprKind::MethodCall(..) => {
if is_never(self.cx, e) {
ControlFlow::Break(())
} else {
ControlFlow::Continue(Descend::Yes)
}
},
ExprKind::If(if_expr, if_then, if_else) => {
let else_diverges = if_else.map_or(false, |ex| expr_diverges(self.cx, ex));
let diverges =
expr_diverges(self.cx, if_expr) || (else_diverges && expr_diverges(self.cx, if_then));
if diverges {
ControlFlow::Break(())
} else {
ControlFlow::Continue(Descend::No)
}
},
ExprKind::Match(match_expr, match_arms, _) => {
let diverges = expr_diverges(self.cx, match_expr)
|| match_arms.iter().all(|arm| {
let guard_diverges = arm.guard.as_ref().map_or(false, |g| expr_diverges(self.cx, g.body()));
guard_diverges || expr_diverges(self.cx, arm.body)
});
if diverges {
ControlFlow::Break(())
} else {
ControlFlow::Continue(Descend::No)
}
},
// Don't continue into loops or labeled blocks, as they are breakable,
// and we'd have to start checking labels.
ExprKind::Block(_, Some(_)) | ExprKind::Loop(..) => ControlFlow::Continue(Descend::No),
// Default: descend
_ => ControlFlow::Continue(Descend::Yes),
};
if let ControlFlow::Continue(Descend::Yes) = self.res {
walk_expr(self, e);
}
}
fn visit_local(&mut self, local: &'tcx Local<'_>) {
// Don't visit the else block of a let/else statement as it will not make
// the statement divergent even though the else block is divergent.
if let Some(init) = local.init {
self.visit_expr(init);
}
}
// Avoid unnecessary `walk_*` calls.
fn visit_ty(&mut self, _: &'tcx Ty<'tcx>) {}
fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {}
fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {}
// Avoid monomorphising all `visit_*` functions.
fn visit_nested_item(&mut self, _: ItemId) {}
}
let mut v = V {
cx,
res: ControlFlow::Continue(Descend::Yes),
};
expr.visit(&mut v);
v.res.is_break()
}
fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: bool) -> bool { fn pat_allowed_for_else(cx: &LateContext<'_>, pat: &'_ Pat<'_>, check_types: bool) -> bool {
// Check whether the pattern contains any bindings, as the // Check whether the pattern contains any bindings, as the
// binding might potentially be used in the body. // binding might potentially be used in the body.

View file

@ -4,7 +4,6 @@ use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
use clippy_utils::source::snippet; use clippy_utils::source::snippet;
use clippy_utils::usage::mutated_variables; use clippy_utils::usage::mutated_variables;
use clippy_utils::{eq_expr_value, higher, match_def_path, paths}; use clippy_utils::{eq_expr_value, higher, match_def_path, paths};
use if_chain::if_chain;
use rustc_ast::ast::LitKind; use rustc_ast::ast::LitKind;
use rustc_hir::def::Res; use rustc_hir::def::Res;
use rustc_hir::intravisit::{walk_expr, Visitor}; use rustc_hir::intravisit::{walk_expr, Visitor};
@ -71,12 +70,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
return; return;
} }
if_chain! { if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr)
if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr); && let ExprKind::MethodCall(_, target_arg, [pattern], _) = cond.kind
if let ExprKind::MethodCall(_, target_arg, [pattern], _) = cond.kind; && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id)
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id); && let ExprKind::Path(target_path) = &target_arg.kind
if let ExprKind::Path(target_path) = &target_arg.kind; {
then {
let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) { let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) {
StripKind::Prefix StripKind::Prefix
} else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) { } else if match_def_path(cx, method_def_id, &paths::STR_ENDS_WITH) {
@ -89,37 +87,44 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
return; return;
}; };
if_chain! { if let Res::Local(hir_id) = target_res
if let Res::Local(hir_id) = target_res; && let Some(used_mutably) = mutated_variables(then, cx)
if let Some(used_mutably) = mutated_variables(then, cx); && used_mutably.contains(&hir_id)
if used_mutably.contains(&hir_id); {
then {
return; return;
} }
}
let strippings = find_stripping(cx, strip_kind, target_res, pattern, then); let strippings = find_stripping(cx, strip_kind, target_res, pattern, then);
if !strippings.is_empty() { if !strippings.is_empty() {
let kind_word = match strip_kind { let kind_word = match strip_kind {
StripKind::Prefix => "prefix", StripKind::Prefix => "prefix",
StripKind::Suffix => "suffix", StripKind::Suffix => "suffix",
}; };
let test_span = expr.span.until(then.span); let test_span = expr.span.until(then.span);
span_lint_and_then(cx, MANUAL_STRIP, strippings[0], &format!("stripping a {kind_word} manually"), |diag| { span_lint_and_then(
cx,
MANUAL_STRIP,
strippings[0],
&format!("stripping a {kind_word} manually"),
|diag| {
diag.span_note(test_span, format!("the {kind_word} was tested here")); diag.span_note(test_span, format!("the {kind_word} was tested here"));
multispan_sugg( multispan_sugg(
diag, diag,
&format!("try using the `strip_{kind_word}` method"), &format!("try using the `strip_{kind_word}` method"),
vec![(test_span, vec![(
format!("if let Some(<stripped>) = {}.strip_{kind_word}({}) ", test_span,
format!(
"if let Some(<stripped>) = {}.strip_{kind_word}({}) ",
snippet(cx, target_arg.span, ".."), snippet(cx, target_arg.span, ".."),
snippet(cx, pattern.span, "..")))] snippet(cx, pattern.span, "..")
.into_iter().chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))), ),
)]
.into_iter()
.chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))),
);
},
); );
});
}
} }
} }
} }
@ -129,16 +134,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
// Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise. // Returns `Some(arg)` if `expr` matches `arg.len()` and `None` otherwise.
fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> { fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
if_chain! { if let ExprKind::MethodCall(_, arg, [], _) = expr.kind
if let ExprKind::MethodCall(_, arg, [], _) = expr.kind; && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id); && match_def_path(cx, method_def_id, &paths::STR_LEN)
if match_def_path(cx, method_def_id, &paths::STR_LEN); {
then {
Some(arg) Some(arg)
} else { } else {
None None
} }
}
} }
// Returns the length of the `expr` if it's a constant string or char. // Returns the length of the `expr` if it's a constant string or char.
@ -201,14 +204,13 @@ fn find_stripping<'tcx>(
impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> { impl<'a, 'tcx> Visitor<'tcx> for StrippingFinder<'a, 'tcx> {
fn visit_expr(&mut self, ex: &'tcx Expr<'_>) { fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
if_chain! { if is_ref_str(self.cx, ex)
if is_ref_str(self.cx, ex); && let unref = peel_ref(ex)
let unref = peel_ref(ex); && let ExprKind::Index(indexed, index, _) = &unref.kind
if let ExprKind::Index(indexed, index, _) = &unref.kind; && let Some(higher::Range { start, end, .. }) = higher::Range::hir(index)
if let Some(higher::Range { start, end, .. }) = higher::Range::hir(index); && let ExprKind::Path(path) = &indexed.kind
if let ExprKind::Path(path) = &indexed.kind; && self.cx.qpath_res(path, ex.hir_id) == self.target
if self.cx.qpath_res(path, ex.hir_id) == self.target; {
then {
match (self.strip_kind, start, end) { match (self.strip_kind, start, end) {
(StripKind::Prefix, Some(start), None) => { (StripKind::Prefix, Some(start), None) => {
if eq_pattern_length(self.cx, self.pattern, start) { if eq_pattern_length(self.cx, self.pattern, start) {
@ -217,20 +219,23 @@ fn find_stripping<'tcx>(
} }
}, },
(StripKind::Suffix, None, Some(end)) => { (StripKind::Suffix, None, Some(end)) => {
if_chain! { if let ExprKind::Binary(
if let ExprKind::Binary(Spanned { node: BinOpKind::Sub, .. }, left, right) = end.kind; Spanned {
if let Some(left_arg) = len_arg(self.cx, left); node: BinOpKind::Sub, ..
if let ExprKind::Path(left_path) = &left_arg.kind; },
if self.cx.qpath_res(left_path, left_arg.hir_id) == self.target; left,
if eq_pattern_length(self.cx, self.pattern, right); right,
then { ) = end.kind
&& let Some(left_arg) = len_arg(self.cx, left)
&& let ExprKind::Path(left_path) = &left_arg.kind
&& self.cx.qpath_res(left_path, left_arg.hir_id) == self.target
&& eq_pattern_length(self.cx, self.pattern, right)
{
self.results.push(ex.span); self.results.push(ex.span);
return; return;
} }
}
}, },
_ => {} _ => {},
}
} }
} }

View file

@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context}; use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context};
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{iter_input_pats, method_chain_args}; use clippy_utils::{iter_input_pats, method_chain_args};
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -163,17 +162,15 @@ fn unit_closure<'tcx>(
cx: &LateContext<'tcx>, cx: &LateContext<'tcx>,
expr: &hir::Expr<'_>, expr: &hir::Expr<'_>,
) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> { ) -> Option<(&'tcx hir::Param<'tcx>, &'tcx hir::Expr<'tcx>)> {
if_chain! { if let hir::ExprKind::Closure(&hir::Closure { fn_decl, body, .. }) = expr.kind
if let hir::ExprKind::Closure(&hir::Closure { fn_decl, body, .. }) = expr.kind; && let body = cx.tcx.hir().body(body)
let body = cx.tcx.hir().body(body); && let body_expr = &body.value
let body_expr = &body.value; && fn_decl.inputs.len() == 1
if fn_decl.inputs.len() == 1; && is_unit_expression(cx, body_expr)
if is_unit_expression(cx, body_expr); && let Some(binding) = iter_input_pats(fn_decl, body).next()
if let Some(binding) = iter_input_pats(fn_decl, body).next(); {
then {
return Some((binding, body_expr)); return Some((binding, body_expr));
} }
}
None None
} }

View file

@ -2,7 +2,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::source::snippet_with_context; use clippy_utils::source::snippet_with_context;
use clippy_utils::ty::is_type_diagnostic_item; use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{higher, is_res_lang_ctor}; use clippy_utils::{higher, is_res_lang_ctor};
use if_chain::if_chain;
use rustc_errors::Applicability; use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind, LangItem, PatKind}; use rustc_hir::{Expr, ExprKind, LangItem, PatKind};
use rustc_lint::{LateContext, LateLintPass}; use rustc_lint::{LateContext, LateLintPass};
@ -56,16 +55,15 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
return; return;
}; };
if_chain! { if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind //check is expr.ok() has type Result<T,E>.ok(, _)
if let ExprKind::MethodCall(ok_path, recv, [], ..) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _) && let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind //get operation
if let PatKind::TupleStruct(ref pat_path, [ok_pat], _) = let_pat.kind; //get operation && ok_path.ident.as_str() == "ok"
if ok_path.ident.as_str() == "ok"; && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result)
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result); && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome)
if is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome); && let ctxt = expr.span.ctxt()
let ctxt = expr.span.ctxt(); && let_expr.span.ctxt() == ctxt
if let_expr.span.ctxt() == ctxt; && let_pat.span.ctxt() == ctxt
if let_pat.span.ctxt() == ctxt; {
then {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
let some_expr_string = snippet_with_context(cx, ok_pat.span, ctxt, "", &mut applicability).0; let some_expr_string = snippet_with_context(cx, ok_pat.span, ctxt, "", &mut applicability).0;
let trimmed_ok = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0; let trimmed_ok = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0;
@ -84,5 +82,4 @@ impl<'tcx> LateLintPass<'tcx> for MatchResultOk {
); );
} }
} }
}
} }

View file

@ -5,7 +5,6 @@ use clippy_utils::visitors::is_local_used;
use clippy_utils::{ use clippy_utils::{
is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators, SpanlessEq,
}; };
use if_chain::if_chain;
use rustc_errors::MultiSpan; use rustc_errors::MultiSpan;
use rustc_hir::LangItem::OptionNone; use rustc_hir::LangItem::OptionNone;
use rustc_hir::{Arm, Expr, Guard, HirId, Let, Pat, PatKind}; use rustc_hir::{Arm, Expr, Guard, HirId, Let, Pat, PatKind};
@ -40,54 +39,55 @@ fn check_arm<'tcx>(
outer_else_body: Option<&'tcx Expr<'tcx>>, outer_else_body: Option<&'tcx Expr<'tcx>>,
) { ) {
let inner_expr = peel_blocks_with_stmt(outer_then_body); let inner_expr = peel_blocks_with_stmt(outer_then_body);
if_chain! { if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr)
if let Some(inner) = IfLetOrMatch::parse(cx, inner_expr); && let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner {
if let Some((inner_scrutinee, inner_then_pat, inner_else_body)) = match inner {
IfLetOrMatch::IfLet(scrutinee, pat, _, els) => Some((scrutinee, pat, els)), IfLetOrMatch::IfLet(scrutinee, pat, _, els) => Some((scrutinee, pat, els)),
IfLetOrMatch::Match(scrutinee, arms, ..) => if_chain! { IfLetOrMatch::Match(scrutinee, arms, ..) => if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none())
// if there are more than two arms, collapsing would be non-trivial // if there are more than two arms, collapsing would be non-trivial
if arms.len() == 2 && arms.iter().all(|a| a.guard.is_none());
// one of the arms must be "wild-like" // one of the arms must be "wild-like"
if let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a)); && let Some(wild_idx) = arms.iter().rposition(|a| arm_is_wild_like(cx, a))
then { {
let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]); let (then, els) = (&arms[1 - wild_idx], &arms[wild_idx]);
Some((scrutinee, then.pat, Some(els.body))) Some((scrutinee, then.pat, Some(els.body)))
} else { } else {
None None
}
}, },
}; }
if outer_pat.span.eq_ctxt(inner_scrutinee.span); && outer_pat.span.eq_ctxt(inner_scrutinee.span)
// match expression must be a local binding // match expression must be a local binding
// match <local> { .. } // match <local> { .. }
if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee)); && let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_scrutinee))
if !pat_contains_or(inner_then_pat); && !pat_contains_or(inner_then_pat)
// the binding must come from the pattern of the containing match arm // the binding must come from the pattern of the containing match arm
// ..<local>.. => match <local> { .. } // ..<local>.. => match <local> { .. }
if let (Some(binding_span), is_innermost_parent_pat_struct) && let (Some(binding_span), is_innermost_parent_pat_struct)
= find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id); = find_pat_binding_and_is_innermost_parent_pat_struct(outer_pat, binding_id)
// the "else" branches must be equal // the "else" branches must be equal
if match (outer_else_body, inner_else_body) { && match (outer_else_body, inner_else_body) {
(None, None) => true, (None, None) => true,
(None, Some(e)) | (Some(e), None) => is_unit_expr(e), (None, Some(e)) | (Some(e), None) => is_unit_expr(e),
(Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b), (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b),
}; }
// the binding must not be used in the if guard // the binding must not be used in the if guard
if outer_guard.map_or( && outer_guard.map_or(
true, true,
|(Guard::If(e) | Guard::IfLet(Let { init: e, .. }))| !is_local_used(cx, *e, binding_id) |(Guard::If(e) | Guard::IfLet(Let { init: e, .. }))| !is_local_used(cx, *e, binding_id)
); )
// ...or anywhere in the inner expression // ...or anywhere in the inner expression
if match inner { && match inner {
IfLetOrMatch::IfLet(_, _, body, els) => { IfLetOrMatch::IfLet(_, _, body, els) => {
!is_local_used(cx, body, binding_id) && els.map_or(true, |e| !is_local_used(cx, e, binding_id)) !is_local_used(cx, body, binding_id) && els.map_or(true, |e| !is_local_used(cx, e, binding_id))
}, },
IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| is_local_used(cx, arm, binding_id)), IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| is_local_used(cx, arm, binding_id)),
}; }
then { {
let msg = format!( let msg = format!(
"this `{}` can be collapsed into the outer `{}`", "this `{}` can be collapsed into the outer `{}`",
if matches!(inner, IfLetOrMatch::Match(..)) { "match" } else { "if let" }, if matches!(inner, IfLetOrMatch::Match(..)) {
"match"
} else {
"if let"
},
if outer_is_match { "match" } else { "if let" }, if outer_is_match { "match" } else { "if let" },
); );
// collapsing patterns need an explicit field name in struct pattern matching // collapsing patterns need an explicit field name in struct pattern matching
@ -97,19 +97,15 @@ fn check_arm<'tcx>(
} else { } else {
String::new() String::new()
}; };
span_lint_and_then( span_lint_and_then(cx, COLLAPSIBLE_MATCH, inner_expr.span, &msg, |diag| {
cx,
COLLAPSIBLE_MATCH,
inner_expr.span,
&msg,
|diag| {
let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]); let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]);
help_span.push_span_label(binding_span, "replace this binding"); help_span.push_span_label(binding_span, "replace this binding");
help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}")); help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}"));
diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern"); diag.span_help(
}, help_span,
"the outer pattern can be modified to include the inner pattern",
); );
} });
} }
} }

View file

@ -8,19 +8,17 @@ use rustc_lint::LateContext;
use super::INFALLIBLE_DESTRUCTURING_MATCH; use super::INFALLIBLE_DESTRUCTURING_MATCH;
pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool { pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool {
if_chain! { if !local.span.from_expansion()
if !local.span.from_expansion(); && let Some(expr) = local.init
if let Some(expr) = local.init; && let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind
if let ExprKind::Match(target, arms, MatchSource::Normal) = expr.kind; && arms.len() == 1
if arms.len() == 1 && arms[0].guard.is_none(); && arms[0].guard.is_none()
if let PatKind::TupleStruct( && let PatKind::TupleStruct(QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind
QPath::Resolved(None, variant_name), args, _) = arms[0].pat.kind; && args.len() == 1
if args.len() == 1; && let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind
if let PatKind::Binding(binding, arg, ..) = strip_pat_refs(&args[0]).kind; && let body = peel_blocks(arms[0].body)
let body = peel_blocks(arms[0].body); && path_to_local_id(body, arg)
if path_to_local_id(body, arg); {
then {
let mut applicability = Applicability::MachineApplicable; let mut applicability = Applicability::MachineApplicable;
span_lint_and_sugg( span_lint_and_sugg(
cx, cx,
@ -40,6 +38,5 @@ pub(crate) fn check(cx: &LateContext<'_>, local: &Local<'_>) -> bool {
); );
return true; return true;
} }
}
false false
} }

View file

@ -21,19 +21,19 @@ fn get_cond_expr<'tcx>(
expr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>,
ctxt: SyntaxContext, ctxt: SyntaxContext,
) -> Option<SomeExpr<'tcx>> { ) -> Option<SomeExpr<'tcx>> {
if_chain! { if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr)
if let Some(block_expr) = peels_blocks_incl_unsafe_opt(expr); && let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind
if let ExprKind::If(cond, then_expr, Some(else_expr)) = block_expr.kind; && let PatKind::Binding(_, target, ..) = pat.kind
if let PatKind::Binding(_,target, ..) = pat.kind; && (is_some_expr(cx, target, ctxt, then_expr) && is_none_expr(cx, else_expr)
if is_some_expr(cx, target, ctxt, then_expr) && is_none_expr(cx, else_expr) || is_none_expr(cx, then_expr) && is_some_expr(cx, target, ctxt, else_expr))
|| is_none_expr(cx, then_expr) && is_some_expr(cx, target, ctxt, else_expr); // check that one expr resolves to `Some(x)`, the other to `None` // check that one expr resolves to `Some(x)`, the other to `None`
then { {
return Some(SomeExpr { return Some(SomeExpr {
expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()), expr: peels_blocks_incl_unsafe(cond.peel_drop_temps()),
needs_unsafe_block: contains_unsafe_block(cx, expr), needs_unsafe_block: contains_unsafe_block(cx, expr),
needs_negated: is_none_expr(cx, then_expr) // if the `then_expr` resolves to `None`, need to negate the cond needs_negated: is_none_expr(cx, then_expr), /* if the `then_expr` resolves to `None`, need to negate the
}) * cond */
} });
}; };
None None
} }

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