diff --git a/compiler/rustc_codegen_gcc/src/common.rs b/compiler/rustc_codegen_gcc/src/common.rs index dd0064d34bc4a..b9b6e51d3b82c 100644 --- a/compiler/rustc_codegen_gcc/src/common.rs +++ b/compiler/rustc_codegen_gcc/src/common.rs @@ -58,13 +58,19 @@ pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> // or is it using a more efficient representation? match bytes.len() % 8 { 0 => { + debug_assert_eq!( + bytes.len() % 8, + 0, + "bytes length is not a multiple of 8, so bytes.as_chunks will have a remainder" + ); let context = &cx.context; let byte_type = context.new_type::(); let typ = new_array_type(context, None, byte_type, bytes.len() as u64 / 8); let elements: Vec<_> = bytes - .chunks_exact(8) - .map(|arr| { - let arr: [u8; 8] = arr.try_into().unwrap(); + .as_chunks::<8>() + .0 + .iter() + .map(|&arr| { context.new_rvalue_from_long( byte_type, // Since we are representing arbitrary byte runs as integers, we need to follow the target @@ -79,13 +85,19 @@ pub fn bytes_in_context<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, bytes: &[u8]) -> context.new_array_constructor(None, typ, &elements) } 4 => { + debug_assert_eq!( + bytes.len() % 4, + 0, + "bytes length is not a multiple of 4, so bytes.as_chunks will have a remainder" + ); let context = &cx.context; let byte_type = context.new_type::(); let typ = new_array_type(context, None, byte_type, bytes.len() as u64 / 4); let elements: Vec<_> = bytes - .chunks_exact(4) - .map(|arr| { - let arr: [u8; 4] = arr.try_into().unwrap(); + .as_chunks::<4>() + .0 + .iter() + .map(|&arr| { context.new_rvalue_from_int( byte_type, match cx.sess().target.options.endian { diff --git a/src/tools/clippy/.github/FUNDING.yml b/src/tools/clippy/.github/FUNDING.yml new file mode 100644 index 0000000000000..16ac7b6eb9bc9 --- /dev/null +++ b/src/tools/clippy/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: rustfoundation +custom: [ "rust-lang.org/funding" ] diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml index 3a99d65233d38..cd90bb0a25f5f 100644 --- a/src/tools/clippy/.github/workflows/clippy_dev.yml +++ b/src/tools/clippy/.github/workflows/clippy_dev.yml @@ -16,7 +16,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/src/tools/clippy/.github/workflows/clippy_mq.yml b/src/tools/clippy/.github/workflows/clippy_mq.yml index b612ea4611a9f..7d313ece05b80 100644 --- a/src/tools/clippy/.github/workflows/clippy_mq.yml +++ b/src/tools/clippy/.github/workflows/clippy_mq.yml @@ -34,7 +34,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -94,7 +94,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -112,7 +112,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false @@ -168,7 +168,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: persist-credentials: false diff --git a/src/tools/clippy/.github/workflows/clippy_pr.yml b/src/tools/clippy/.github/workflows/clippy_pr.yml index f9e882d9757ce..5658e041e03cf 100644 --- a/src/tools/clippy/.github/workflows/clippy_pr.yml +++ b/src/tools/clippy/.github/workflows/clippy_pr.yml @@ -24,7 +24,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/src/tools/clippy/.github/workflows/deploy.yml b/src/tools/clippy/.github/workflows/deploy.yml index 872931160c35f..b95cbbddd4b32 100644 --- a/src/tools/clippy/.github/workflows/deploy.yml +++ b/src/tools/clippy/.github/workflows/deploy.yml @@ -25,13 +25,13 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: ref: ${{ env.TARGET_BRANCH }} path: 'out' diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml index 981fecd17c988..210f10b7b8804 100644 --- a/src/tools/clippy/.github/workflows/lintcheck.yml +++ b/src/tools/clippy/.github/workflows/lintcheck.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: fetch-depth: 2 # Unsetting this would make so that any malicious package could get our Github Token @@ -80,7 +80,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false @@ -113,7 +113,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml index d4dc80efe79de..532af9dff83f2 100644 --- a/src/tools/clippy/.github/workflows/remark.yml +++ b/src/tools/clippy/.github/workflows/remark.yml @@ -14,7 +14,7 @@ jobs: steps: # Setup - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: # Unsetting this would make so that any malicious package could get our Github Token persist-credentials: false diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md index 337c900aaf0c8..689d56d0fb7c6 100644 --- a/src/tools/clippy/CHANGELOG.md +++ b/src/tools/clippy/CHANGELOG.md @@ -6670,6 +6670,7 @@ Released 2018-09-13 [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow +[`by_ref_peekable_peek`]: https://rust-lang.github.io/rust-clippy/master/index.html#by_ref_peekable_peek [`byte_char_slices`]: https://rust-lang.github.io/rust-clippy/master/index.html#byte_char_slices [`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth @@ -6694,6 +6695,7 @@ Released 2018-09-13 [`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp [`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp [`checked_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#checked_conversions +[`chunks_exact_to_as_chunks`]: https://rust-lang.github.io/rust-clippy/master/index.html#chunks_exact_to_as_chunks [`clear_with_drain`]: https://rust-lang.github.io/rust-clippy/master/index.html#clear_with_drain [`clone_double_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_double_ref [`clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#clone_on_copy @@ -7457,6 +7459,7 @@ Released 2018-09-13 [`unnecessary_to_owned`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_to_owned [`unnecessary_trailing_comma`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_trailing_comma [`unnecessary_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap +[`unnecessary_unwrap_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap_unchecked [`unnecessary_wraps`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_wraps [`unneeded_field_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_field_pattern [`unneeded_struct_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#unneeded_struct_pattern @@ -7571,6 +7574,7 @@ Released 2018-09-13 [`avoid-breaking-exported-api`]: https://doc.rust-lang.org/clippy/lint_configuration.html#avoid-breaking-exported-api [`await-holding-invalid-types`]: https://doc.rust-lang.org/clippy/lint_configuration.html#await-holding-invalid-types [`cargo-ignore-publish`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cargo-ignore-publish +[`check-grouped-late-init`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-grouped-late-init [`check-incompatible-msrv-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-incompatible-msrv-in-tests [`check-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-inconsistent-struct-field-initializers [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items @@ -7608,7 +7612,6 @@ Released 2018-09-13 [`module-items-ordered-within-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-items-ordered-within-groupings [`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv [`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit -[`profiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#profiles [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior [`recursive-self-in-type-definitions`]: https://doc.rust-lang.org/clippy/lint_configuration.html#recursive-self-in-type-definitions [`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline diff --git a/src/tools/clippy/book/src/continuous_integration/github_actions.md b/src/tools/clippy/book/src/continuous_integration/github_actions.md index bed0f66bab33a..e198a13b38d24 100644 --- a/src/tools/clippy/book/src/continuous_integration/github_actions.md +++ b/src/tools/clippy/book/src/continuous_integration/github_actions.md @@ -15,7 +15,7 @@ jobs: clippy_check: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Run Clippy run: cargo clippy --all-targets --all-features ``` diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md index e123d7ba5704d..6c6ccd4747c1f 100644 --- a/src/tools/clippy/book/src/lint_configuration.md +++ b/src/tools/clippy/book/src/lint_configuration.md @@ -452,6 +452,37 @@ For internal testing only, ignores the current `publish` settings in the Cargo m * [`cargo_common_metadata`](https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata) +## `check-grouped-late-init` +Whether to check for grouped late initializations from multiple `let` statements. + +#### Example +```rust +let a; +let b; +if true { + a = 1; + b = 2; +} else { + a = 3; + b = 4; +} +``` +Use instead: +```rust +let (a, b) = if true { + (1, 2) +} else { + (3, 4) +}; +``` + +**Default Value:** `true` + +--- +**Affected lints:** +* [`needless_late_init`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_late_init) + + ## `check-incompatible-msrv-in-tests` Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code. @@ -986,28 +1017,6 @@ The minimum size (in bytes) to consider a type for passing by reference instead * [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value) -## `profiles` -Named profiles of disallowed items (unrelated to Cargo build profiles). - -#### Example - -```toml -[profiles.persistent] -disallowed-methods = [{ path = "std::env::temp_dir" }] -disallowed-types = [{ path = "std::time::Instant", reason = "use our custom time API" }] - -[profiles.single_threaded] -disallowed-methods = [{ path = "std::thread::spawn" }] -``` - -**Default Value:** `{}` - ---- -**Affected lints:** -* [`disallowed_methods`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_methods) -* [`disallowed_types`](https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types) - - ## `pub-underscore-fields-behavior` Lint "public" fields in a struct that are prefixed with an underscore based on their exported visibility, or whether they are marked as "pub". diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs index 47bc7b572c9d0..d910eb47752b5 100644 --- a/src/tools/clippy/clippy_config/src/conf.rs +++ b/src/tools/clippy/clippy_config/src/conf.rs @@ -1,13 +1,12 @@ use crate::ClippyConfiguration; use crate::types::{ - DisallowedPath, DisallowedPathWithoutReplacement, DisallowedProfile, InherentImplLintScope, MacroMatcher, - MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, + DisallowedPath, DisallowedPathWithoutReplacement, InherentImplLintScope, MacroMatcher, MatchLintBehaviour, + PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering, SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind, SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds, SourceItemOrderingWithinModuleItemGroupings, }; use clippy_utils::msrvs::Msrv; use itertools::Itertools; -use rustc_data_structures::fx::FxHashMap; use rustc_errors::Applicability; use rustc_session::Session; use rustc_span::edit_distance::edit_distance; @@ -223,74 +222,12 @@ macro_rules! deserialize { }}; } -macro_rules! parse_conf_value { - ( - $map:expr, - $ty:ty, - $errors:expr, - $file:expr, - $field_span:expr, - profiles @[$($profiles:expr)?], - disallowed @[$($disallowed:expr)?] - ) => { - parse_conf_value_impl!( - $map, - $ty, - $errors, - $file, - $field_span, - ($($profiles)?), - ($($disallowed)?) - ) - }; -} - -macro_rules! parse_conf_value_impl { - ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, (), ()) => {{ - let _ = &$field_span; - deserialize!($map, $ty, $errors, $file) - }}; - ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, ($profiles:expr), ()) => {{ - let raw_value = $map.next_value::()?; - let value_span = $field_span.clone(); - let toml::Value::Table(table) = raw_value else { - $errors.push(ConfError::spanned( - $file, - "expected table with named profiles", - None, - value_span.clone(), - )); - continue; - }; - - let map = parse_profiles(table, $file, value_span.clone(), &mut $errors); - - (map, value_span) - }}; - ($map:expr, $ty:ty, $errors:expr, $file:expr, $field_span:expr, (), ($disallowed:expr)) => {{ - let _ = &$field_span; - deserialize!($map, $ty, $errors, $file, $disallowed) - }}; - ( - $map:expr, - $ty:ty, - $errors:expr, - $file:expr, - $field_span:expr, - ($profiles:expr), - ($disallowed:expr) - ) => { - compile_error!("field cannot specify both profiles and disallowed-paths attributes") - }; -} - macro_rules! define_Conf { ($( $(#[doc = $doc:literal])+ $(#[conf_deprecated($dep:literal, $new_conf:ident)])? $(#[default_text = $default_text:expr])? $(#[disallowed_paths_allow_replacements = $replacements_allowed:expr])? - $(#[profiles = $profiles:expr])? $(#[lints($($for_lints:ident),* $(,)?)])? $name:ident: $ty:ty = $default:expr, )*) => { @@ -345,20 +282,10 @@ macro_rules! define_Conf { match field { $(Field::$name => { - let field_span = name.span(); // Is this a deprecated field, i.e., is `$dep` set? If so, push a warning. $(warnings.push(ConfError::spanned(self.0, format!("deprecated field `{}`. {}", name.get_ref(), $dep), None, name.span()));)? - let (value, value_span) = parse_conf_value!( - map, - $ty, - errors, - self.0, - field_span, - // Disallowed-profile table parsing is special-cased to preserve spans for - // diagnostics in disallowed-path entries. - profiles @[$($profiles)?], - disallowed @[$($replacements_allowed)?] - ); + let (value, value_span) = + deserialize!(map, $ty, errors, self.0 $(, $replacements_allowed)?); // Was this field set previously? if $name.is_some() { errors.push(ConfError::spanned(self.0, format!("duplicate field `{}`", name.get_ref()), None, name.span())); @@ -415,121 +342,6 @@ fn span_from_toml_range(file: &SourceFile, span: Range) -> Span { ) } -fn parse_profiles( - table: toml::value::Table, - file: &SourceFile, - value_span: Range, - errors: &mut Vec, -) -> FxHashMap { - let mut profiles = FxHashMap::default(); - let config_span = span_from_toml_range(file, value_span.clone()); - - for (profile_name, profile_value) in table { - let toml::Value::Table(mut profile_table) = profile_value else { - errors.push(ConfError::spanned( - file, - format!("invalid profile `{profile_name}`: expected table"), - None, - value_span.clone(), - )); - continue; - }; - - let disallowed_methods = match profile_table - .remove("disallowed-methods") - .or_else(|| profile_table.remove("disallowed_methods")) - { - Some(value) => parse_profile_list( - file, - &profile_name, - "disallowed-methods", - value, - value_span.clone(), - config_span, - errors, - ), - None => Vec::new(), - }; - - let disallowed_types = match profile_table - .remove("disallowed-types") - .or_else(|| profile_table.remove("disallowed_types")) - { - Some(value) => parse_profile_list( - file, - &profile_name, - "disallowed-types", - value, - value_span.clone(), - config_span, - errors, - ), - None => Vec::new(), - }; - - if !profile_table.is_empty() { - let keys = profile_table.keys().map(String::as_str).collect::>().join(", "); - errors.push(ConfError::spanned( - file, - format!("profile `{profile_name}` has unknown keys: {keys}"), - None, - value_span.clone(), - )); - } - - profiles.insert( - profile_name, - DisallowedProfile { - disallowed_methods, - disallowed_types, - }, - ); - } - - profiles -} - -fn parse_profile_list( - file: &SourceFile, - profile_name: &str, - key_name: &str, - value: toml::Value, - value_span: Range, - config_span: Span, - errors: &mut Vec, -) -> Vec { - let toml::Value::Array(entries) = value else { - errors.push(ConfError::spanned( - file, - format!("profile `{profile_name}`: `{key_name}` must be an array"), - None, - value_span, - )); - return Vec::new(); - }; - - let mut disallowed = Vec::with_capacity(entries.len()); - for entry in entries { - match DisallowedPath::deserialize(entry.clone()) { - Ok(mut path) => { - path.set_span(config_span); - disallowed.push(path); - }, - Err(err) => errors.push(ConfError::spanned( - file, - format!( - "profile `{profile_name}`: {}", - err.to_string().replace('\n', " ").trim() - ), - None, - value_span.clone(), - )), - } - } - - disallowed -} - define_Conf! { /// Which crates to allow absolute paths from #[lints(absolute_paths)] @@ -742,6 +554,30 @@ define_Conf! { /// For internal testing only, ignores the current `publish` settings in the Cargo manifest. #[lints(cargo_common_metadata)] cargo_ignore_publish: bool = false, + /// Whether to check for grouped late initializations from multiple `let` statements. + /// + /// #### Example + /// ```rust + /// let a; + /// let b; + /// if true { + /// a = 1; + /// b = 2; + /// } else { + /// a = 3; + /// b = 4; + /// } + /// ``` + /// Use instead: + /// ```rust + /// let (a, b) = if true { + /// (1, 2) + /// } else { + /// (3, 4) + /// }; + /// ``` + #[lints(needless_late_init)] + check_grouped_late_init: bool = true, /// Whether to check MSRV compatibility in `#[test]` and `#[cfg(test)]` code. #[lints(incompatible_msrv)] check_incompatible_msrv_in_tests: bool = false, @@ -1029,21 +865,6 @@ define_Conf! { /// The minimum size (in bytes) to consider a type for passing by reference instead of by value. #[lints(large_types_passed_by_value)] pass_by_value_size_limit: u64 = 256, - /// Named profiles of disallowed items (unrelated to Cargo build profiles). - /// - /// #### Example - /// - /// ```toml - /// [profiles.persistent] - /// disallowed-methods = [{ path = "std::env::temp_dir" }] - /// disallowed-types = [{ path = "std::time::Instant", reason = "use our custom time API" }] - /// - /// [profiles.single_threaded] - /// disallowed-methods = [{ path = "std::thread::spawn" }] - /// ``` - #[profiles = true] - #[lints(disallowed_methods, disallowed_types)] - profiles: FxHashMap = FxHashMap::default(), /// Lint "public" fields in a struct that are prefixed with an underscore based on their /// exported visibility, or whether they are marked as "pub". #[lints(pub_underscore_fields)] diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs index 5eaa44ddd51d7..8d9326a904b1e 100644 --- a/src/tools/clippy/clippy_config/src/types.rs +++ b/src/tools/clippy/clippy_config/src/types.rs @@ -57,15 +57,6 @@ impl<'de, const REPLACEMENT_ALLOWED: bool> Deserialize<'de> for DisallowedPath, - #[serde(default, alias = "disallowed_types")] - pub disallowed_types: Vec, -} - // `DisallowedPathEnum` is an implementation detail to enable the `Deserialize` implementation just // above. `DisallowedPathEnum` is not meant to be used outside of this file. #[derive(Debug, Deserialize, Serialize)] diff --git a/src/tools/clippy/clippy_lints/src/absolute_paths.rs b/src/tools/clippy/clippy_lints/src/absolute_paths.rs index fd515939dfb8e..cf2059eecc079 100644 --- a/src/tools/clippy/clippy_lints/src/absolute_paths.rs +++ b/src/tools/clippy/clippy_lints/src/absolute_paths.rs @@ -12,7 +12,7 @@ use rustc_span::symbol::kw; declare_clippy_lint! { /// ### What it does - /// Checks for usage of items through absolute paths, like `std::env::current_dir`. + /// Checks for usage of items through absolute paths, like `std::f64::consts::PI`. /// /// ### Why restrict this? /// Many codebases have their own style when it comes to importing, but one that is seldom used diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs index 6e57d0608bed8..118c3c23d727d 100644 --- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs +++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs @@ -52,7 +52,9 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants { _ => return, } && let Some((condition, _)) = find_assert_args(cx, e, macro_call.expn) - && is_const_evaluatable(cx, condition) + // Check if the whole expression can be moved into a const context. + // Note that const eval can evaluate things which cannot be moved (e.g. `false && x`). + && is_const_evaluatable(cx.tcx, cx.typeck_results(), condition) && let Some((Constant::Bool(assert_val), const_src)) = ConstEvalCtxt::new(cx).eval_with_source(condition, macro_call.span.ctxt()) && let in_const_context = is_inside_always_const_context(cx.tcx, e.hir_id) diff --git a/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs index 7eff5eccfa138..91e2689a14731 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs @@ -1,6 +1,6 @@ use super::{Attribute, NON_MINIMAL_CFG}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::{MetaItemInner, MetaItemKind}; use rustc_errors::Applicability; use rustc_lint::EarlyContext; @@ -29,7 +29,7 @@ fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[MetaItemInner]) { meta.span, "unneeded sub `cfg` when there is only one condition", |diag| { - if let Some(snippet) = list[0].span().get_source_text(cx) { + if let Some(snippet) = list[0].span().get_text(cx) { diag.span_suggestion( meta.span, "try", diff --git a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs index 6efc931df2429..4c97c03471aa9 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs @@ -1,6 +1,6 @@ use super::{Attribute, UNNECESSARY_CLIPPY_CFG}; use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use itertools::Itertools; use rustc_ast::AttrStyle; use rustc_errors::Applicability; @@ -32,7 +32,7 @@ pub(super) fn check( return; } if nb_items == clippy_lints.len() { - if let Some(snippet) = behind_cfg_attr.span.get_source_text(cx) { + if let Some(snippet) = behind_cfg_attr.span.get_text(cx) { span_lint_and_sugg( cx, UNNECESSARY_CLIPPY_CFG, @@ -48,7 +48,7 @@ pub(super) fn check( ); } } else { - let snippet = clippy_lints.iter().filter_map(|sp| sp.get_source_text(cx)).join(","); + let snippet = clippy_lints.iter().filter_map(|sp| sp.get_text(cx)).join(","); span_lint_and_note( cx, UNNECESSARY_CLIPPY_CFG, diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs index 2d56086a96024..60cdcd4a85817 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs @@ -1,7 +1,7 @@ use super::USELESS_ATTRIBUTE; use super::utils::{is_lint_level, is_word, namespace_and_lint}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, first_line_of_span}; +use clippy_utils::source::{SpanExt, first_line_of_span}; use clippy_utils::sym; use rustc_ast::{Attribute, Item, ItemKind}; use rustc_errors::Applicability; @@ -74,7 +74,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { } let line_span = first_line_of_span(cx, attr.span); - if let Some(src) = line_span.get_source_text(cx) + if let Some(src) = line_span.get_text(cx) && src.contains("#[") { #[expect(clippy::collapsible_span_lint_calls)] diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index 2f2382d9a50ed..efd453b3276cc 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then}; use clippy_utils::higher::has_let_expr; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::{eq_expr_value, sym}; @@ -157,30 +157,30 @@ fn check_inverted_bool_in_condition( let suggestion = match (left.kind, right.kind) { (ExprKind::Unary(UnOp::Not, left_sub), ExprKind::Unary(UnOp::Not, right_sub)) => { - let Some(left) = left_sub.span.get_source_text(cx) else { + let Some(left) = left_sub.span.get_text(cx) else { return; }; - let Some(right) = right_sub.span.get_source_text(cx) else { + let Some(right) = right_sub.span.get_text(cx) else { return; }; let Some(op) = bin_op_eq_str(op) else { return }; format!("{left} {op} {right}") }, (ExprKind::Unary(UnOp::Not, left_sub), _) => { - let Some(left) = left_sub.span.get_source_text(cx) else { + let Some(left) = left_sub.span.get_text(cx) else { return; }; - let Some(right) = right.span.get_source_text(cx) else { + let Some(right) = right.span.get_text(cx) else { return; }; let Some(op) = inverted_bin_op_eq_str(op) else { return }; format!("{left} {op} {right}") }, (_, ExprKind::Unary(UnOp::Not, right_sub)) => { - let Some(left) = left.span.get_source_text(cx) else { + let Some(left) = left.span.get_text(cx) else { return; }; - let Some(right) = right_sub.span.get_source_text(cx) else { + let Some(right) = right_sub.span.get_text(cx) else { return; }; let Some(op) = inverted_bin_op_eq_str(op) else { return }; @@ -392,12 +392,8 @@ impl SuggestContext<'_, '_, '_> { } }, &Term(n) => { - self.output.push_str( - &self.terminals[n as usize] - .span - .source_callsite() - .get_source_text(self.cx)?, - ); + self.output + .push_str(&self.terminals[n as usize].span.source_callsite().get_text(self.cx)?); }, } Some(()) @@ -452,10 +448,7 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio .map(|arg| simplify_not(cx, curr_msrv, arg)) .collect::>>()? .join(", "); - Some(format!( - "{}.{neg_method}({negated_args})", - receiver.span.get_source_text(cx)? - )) + Some(format!("{}.{neg_method}({negated_args})", receiver.span.get_text(cx)?)) }) }, ExprKind::Closure(closure) => { @@ -463,13 +456,13 @@ fn simplify_not(cx: &LateContext<'_>, curr_msrv: Msrv, expr: &Expr<'_>) -> Optio let params = body .params .iter() - .map(|param| param.span.get_source_text(cx).map(|t| t.to_string())) + .map(|param| param.span.get_text(cx).map(|t| t.to_string())) .collect::>>()? .join(", "); let negated = simplify_not(cx, curr_msrv, body.value)?; Some(format!("|{params}| {negated}")) }, - ExprKind::Unary(UnOp::Not, expr) => expr.span.get_source_text(cx).map(|t| t.to_string()), + ExprKind::Unary(UnOp::Not, expr) => expr.span.get_text(cx).map(|t| t.to_string()), _ => None, } } diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs index 947b99696bb1f..13ae80868d9b5 100644 --- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs +++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs @@ -1,6 +1,6 @@ use crate::reference::DEREF_ADDROF; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::implements_trait; use clippy_utils::{ get_enclosing_closure, get_parent_expr, is_expr_temporary_value, is_from_proc_macro, is_lint_allowed, is_mutable, @@ -82,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef { // If the new borrow might be itself borrowed mutably and the original reference is not a temporary // value, do not propose to use it directly. && (is_expr_temporary_value(cx, deref_target) || !potentially_bound_to_mutable_ref(cx, e)) - && let Some(deref_text) = deref_target.span.get_source_text(cx) + && let Some(deref_text) = deref_target.span.get_text(cx) { // `&*x` can be needed to shorten the borrow of `x`. Replacing it with `x` can be // incorrect when `x` is a closure-captured upvar (e.g. a closure returning another diff --git a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs index adc43e282fc5c..f23e789cf5d64 100644 --- a/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs +++ b/src/tools/clippy/clippy_lints/src/casts/as_ptr_cast_mut.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).instantiate_identity().skip_norm_wip() && let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next() && let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind() - && let Some(recv) = receiver.span.get_source_text(cx) + && let Some(recv) = receiver.span.get_text(cx) { // `as_mut_ptr` might not exist let applicability = Applicability::MaybeIncorrect; diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs index c924fba6b5c85..d2bf6db751320 100644 --- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs +++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_in_const_context; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sugg::Sugg; use clippy_utils::ty::is_isize_or_usize; use rustc_errors::Applicability; @@ -40,7 +40,7 @@ pub(super) fn check( diag.help("an `as` cast can become silently lossy if the types change in the future"); let mut applicability = Applicability::MachineApplicable; let from_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "", &mut applicability); - let Some(ty) = hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt()).get_source_text(cx) else { + let Some(ty) = hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt()).get_text(cx) else { return; }; match cast_to_hir.kind { diff --git a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs index 55b0945f0962b..36d63ce63195b 100644 --- a/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/manual_dangling_ptr.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{expr_or_init, std_or_core, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -25,7 +25,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: let sugg = if let TyKind::Infer(()) = ptr_ty.ty.kind { format!("{std_or_core}::{sugg_fn}()") - } else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_source_text(cx) { + } else if let Some(mut_ty_snip) = ptr_ty.ty.span.get_text(cx) { format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") } else { return; diff --git a/src/tools/clippy/clippy_lints/src/casts/needless_type_cast.rs b/src/tools/clippy/clippy_lints/src/casts/needless_type_cast.rs index 844d4c7acbe7f..5cfbffc6c3f66 100644 --- a/src/tools/clippy/clippy_lints/src/casts/needless_type_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/needless_type_cast.rs @@ -249,7 +249,7 @@ fn can_coerce_to_target_type(expr: &Expr<'_>) -> bool { fn check_binding_usages<'a>(cx: &LateContext<'a>, body: &Body<'a>, hir_id: HirId, binding_info: &BindingInfo<'a>) { let mut usages = Vec::new(); - for_each_expr(cx, body.value, |expr| { + for_each_expr(cx.tcx, body.value, |expr| { if let ExprKind::Path(ref qpath) = expr.kind && !expr.span.from_expansion() && let Res::Local(id) = cx.qpath_res(qpath, expr.hir_id) diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs index 333f31ba00eac..c14bd056de393 100644 --- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs +++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::numeric_literal::NumericLiteral; use clippy_utils::res::MaybeResPath as _; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; +use clippy_utils::source::{SpanExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::has_enclosing_paren; use clippy_utils::visitors::{Visitable, for_each_expr_without_closures}; use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, sym}; @@ -113,7 +113,7 @@ pub(super) fn check<'tcx>( let literal_str = &cast_str; if let LitKind::Int(n, _) = lit.node - && let Some(src) = cast_expr.span.get_source_text(cx) + && let Some(src) = cast_expr.span.get_text(cx) && cast_to.is_floating_point() && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) && let from_nbits = 128 - n.get().leading_zeros() @@ -140,7 +140,7 @@ pub(super) fn check<'tcx>( | LitKind::Float(_, LitFloatType::Suffixed(_)) if cast_from.kind() == cast_to.kind() => { - if let Some(src) = cast_expr.span.get_source_text(cx) + if let Some(src) = cast_expr.span.get_text(cx) && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) { lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to); @@ -232,7 +232,7 @@ fn lint_unnecessary_cast( // (-1).foo() instead of -1.foo()) let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) && let ExprKind::MethodCall(..) = parent_expr.kind - && literal_str.starts_with('-') + && literal_str.starts_with(['-', '!']) { format!("({literal_str}_{cast_to})") } else { @@ -253,7 +253,7 @@ fn lint_unnecessary_cast( fn get_numeric_literal<'e>(expr: &'e Expr<'e>) -> Option { match expr.kind { ExprKind::Lit(lit) => Some(lit), - ExprKind::Unary(UnOp::Neg, e) => { + ExprKind::Unary(UnOp::Neg | UnOp::Not, e) => { if let ExprKind::Lit(lit) = e.kind { Some(lit) } else { diff --git a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs index f4738e7b0d51d..90290caf10bf7 100644 --- a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs +++ b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core}; use rustc_errors::Applicability; use rustc_hir::{Expr, Mutability, Ty, TyKind}; @@ -21,7 +21,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_> let sugg = if let TyKind::Infer(()) = mut_ty.ty.kind { format!("{std_or_core}::{sugg_fn}()") - } else if let Some(mut_ty_snip) = mut_ty.ty.span.get_source_text(cx) { + } else if let Some(mut_ty_snip) = mut_ty.ty.span.get_text(cx) { format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()") } else { return; diff --git a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs index 4f663f4aa9098..24955ac3c6258 100644 --- a/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs +++ b/src/tools/clippy/clippy_lints/src/cloned_ref_to_slice_refs.rs @@ -64,15 +64,8 @@ impl<'a> ClonedRefToSliceRefs<'a> { impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) { - if self.msrv.meets(cx, { - if is_in_const_context(cx) { - msrvs::CONST_SLICE_FROM_REF - } else { - msrvs::SLICE_FROM_REF - } - }) - // `&[foo.clone()]` expressions - && let ExprKind::AddrOf(_, mutability, arr) = &expr.kind + // `&[foo.clone()]` expressions + if let ExprKind::AddrOf(_, mutability, arr) = &expr.kind // mutable references would have a different meaning && mutability.is_not() @@ -81,10 +74,18 @@ impl<'tcx> LateLintPass<'tcx> for ClonedRefToSliceRefs<'_> { // check for clones && let ExprKind::MethodCall(path, recv, _, _) = item.kind + + && self.msrv.meets(cx, { + if is_in_const_context(cx) { + msrvs::CONST_SLICE_FROM_REF + } else { + msrvs::SLICE_FROM_REF + } + }) && let Some(adjustment) = is_needless_clone_or_equivalent(cx, recv, path.ident.name, item.hir_id) // check for immutability or purity - && (!is_mutable(cx, recv) || is_const_evaluatable(cx, recv)) + && (!is_mutable(cx, recv) || is_const_evaluatable(cx.tcx, cx.typeck_results(), recv)) // get appropriate crate for `slice::from_ref` && let Some(builtin_crate) = clippy_utils::std_or_core(cx) diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 95b99c1d2435f..c330ed3d5a0a4 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{LimitStack, get_async_fn_body, sym}; use core::ops::ControlFlow; diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index baa13b2133277..6def41b775d1d 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::Msrv; -use clippy_utils::source::{HasSession, IntoSpan as _, SpanRangeExt, snippet, snippet_block_with_applicability}; +use clippy_utils::source::{IntoSpan as _, SpanExt, snippet, snippet_block_with_applicability}; use clippy_utils::{can_use_if_let_chains, span_contains_cfg, span_contains_non_whitespace, sym, tokenize_with_text}; use rustc_ast::{BinOpKind, MetaItemInner}; use rustc_errors::Applicability; @@ -319,7 +319,7 @@ pub(super) fn parens_around(expr: &Expr<'_>) -> Vec<(Span, String)> { } } -fn span_extract_keyword(cx: &impl HasSession, span: Span, keyword: &str) -> Option { +fn span_extract_keyword(cx: &LateContext<'_>, span: Span, keyword: &str) -> Option { span.with_source_text(cx, |snippet| { tokenize_with_text(snippet) .filter(|(t, s, _)| matches!(t, TokenKind::Ident if *s == keyword)) @@ -335,7 +335,7 @@ fn span_extract_keyword(cx: &impl HasSession, span: Span, keyword: &str) -> Opti } /// Peel the parentheses from an `if` expression, e.g. `((if true {} else {}))`. -pub(super) fn peel_parens(cx: &impl HasSession, mut span: Span) -> (Span, Span, Span) { +pub(super) fn peel_parens(cx: &LateContext<'_>, mut span: Span) -> (Span, Span, Span) { use crate::rustc_span::Pos; let start = span.shrink_to_lo(); diff --git a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs index ddd3e8b805d19..4ddd396c50ce4 100644 --- a/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs +++ b/src/tools/clippy/clippy_lints/src/collection_is_never_read.rs @@ -79,7 +79,7 @@ fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirI let mut has_read_access = false; // Inspect all expressions and sub-expressions in the block. - for_each_expr(cx, block, |expr| { + for_each_expr(cx.tcx, block, |expr| { // Ignore expressions that are not simply `id`. if expr.res_local_id() != Some(id) { return ControlFlow::Continue(()); diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs index 6daa70f390aaa..51a848d022d80 100644 --- a/src/tools/clippy/clippy_lints/src/declared_lints.rs +++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs @@ -361,11 +361,13 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::mem_replace::MEM_REPLACE_WITH_DEFAULT_INFO, crate::mem_replace::MEM_REPLACE_WITH_UNINIT_INFO, crate::methods::BIND_INSTEAD_OF_MAP_INFO, + crate::methods::BY_REF_PEEKABLE_PEEK_INFO, crate::methods::BYTES_COUNT_TO_LEN_INFO, crate::methods::BYTES_NTH_INFO, crate::methods::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS_INFO, crate::methods::CHARS_LAST_CMP_INFO, crate::methods::CHARS_NEXT_CMP_INFO, + crate::methods::CHUNKS_EXACT_TO_AS_CHUNKS_INFO, crate::methods::CLEAR_WITH_DRAIN_INFO, crate::methods::CLONE_ON_COPY_INFO, crate::methods::CLONE_ON_REF_PTR_INFO, @@ -507,6 +509,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[ crate::methods::UNNECESSARY_RESULT_MAP_OR_ELSE_INFO, crate::methods::UNNECESSARY_SORT_BY_INFO, crate::methods::UNNECESSARY_TO_OWNED_INFO, + crate::methods::UNNECESSARY_UNWRAP_UNCHECKED_INFO, crate::methods::UNWRAP_OR_DEFAULT_INFO, crate::methods::UNWRAP_USED_INFO, crate::methods::USELESS_ASREF_INFO, diff --git a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs index c831f96443c60..9e141bd945db0 100644 --- a/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs +++ b/src/tools/clippy/clippy_lints/src/default_constructed_unit_structs.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_ty_alias; -use clippy_utils::source::SpanRangeExt as _; +use clippy_utils::source::SpanExt as _; use hir::ExprKind; use hir::def::Res; use rustc_errors::Applicability; @@ -78,7 +78,7 @@ impl LateLintPass<'_> for DefaultConstructedUnitStructs { && !base.is_suggestable_infer_ty() { let mut removals = vec![(expr.span.with_lo(qpath.qself_span().hi()), String::new())]; - if expr.span.check_source_text(cx, |s| s.starts_with('<')) { + if expr.span.check_text(cx, |s| s.starts_with('<')) { // Remove `<`, '>` has already been removed by the existing removal expression. removals.push((expr.span.with_hi(qpath.qself_span().lo()), String::new())); } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs index 58cc318d57ccd..e2fd71b7d990f 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs @@ -1,18 +1,13 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::disallowed_profiles::{ProfileEntry, ProfileResolver}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths::PathNS; -use clippy_utils::sym; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::smallvec::SmallVec; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol}; declare_clippy_lint! { /// ### What it does @@ -60,19 +55,6 @@ declare_clippy_lint! { /// let mut xs = Vec::new(); // Vec::new is _not_ disallowed in the config. /// xs.push(123); // Vec::push is _not_ disallowed in the config. /// ``` - /// - /// Disallowed profiles allow scoping different disallow lists: - /// ```toml - /// [profiles.forward_pass] - /// disallowed-methods = [{ path = "crate::devices::Buffer::copy_to_host", reason = "Forward code must not touch host buffers" }] - /// ``` - /// - /// ```rust,ignore - /// #[clippy::disallowed_profile("forward_pass")] - /// fn evaluate() { - /// // Method calls in this function use the `forward_pass` profile. - /// } - /// ``` #[clippy::version = "1.49.0"] pub DISALLOWED_METHODS, style, @@ -82,22 +64,12 @@ declare_clippy_lint! { impl_lint_pass!(DisallowedMethods => [DISALLOWED_METHODS]); pub struct DisallowedMethods { - default: DefIdMap<(&'static str, &'static DisallowedPath)>, - /// Lookup per profile that declares a non-empty `disallowed_methods` list. Profiles - /// declared in `[profiles.*]` but without `disallowed_methods` entries are absent here. - profiles: FxHashMap>, - /// Every profile name declared in `[profiles.*]`, regardless of whether it contributes - /// to this lint. Used to suppress the "unknown profile" warning for profiles that exist - /// in config but only define entries for other lints (e.g. `disallowed_types`). - known_profiles: FxHashSet, - profile_cache: ProfileResolver, - warned_unknown_profiles: FxHashSet, + disallowed: DefIdMap<(&'static str, &'static DisallowedPath)>, } impl DisallowedMethods { - #[allow(rustc::potential_query_instability)] // Profiles are sorted for deterministic iteration. pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let (default, _) = create_disallowed_map( + let (disallowed, _) = create_disallowed_map( tcx, &conf.disallowed_methods, PathNS::Value, @@ -110,62 +82,7 @@ impl DisallowedMethods { "function", false, ); - - let mut profiles = FxHashMap::default(); - let mut known_profiles = FxHashSet::default(); - let mut profile_entries: Vec<_> = conf.profiles.iter().collect(); - profile_entries.sort_by_key(|(a, _)| *a); - for (name, profile) in profile_entries { - let symbol = Symbol::intern(name.as_str()); - known_profiles.insert(symbol); - - let paths = profile.disallowed_methods.as_slice(); - if paths.is_empty() { - continue; - } - - let (map, _) = create_disallowed_map( - tcx, - paths, - PathNS::Value, - |def_kind| { - matches!( - def_kind, - DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn - ) - }, - "function", - false, - ); - profiles.insert(symbol, map); - } - - Self { - default, - profiles, - known_profiles, - profile_cache: ProfileResolver::default(), - warned_unknown_profiles: FxHashSet::default(), - } - } - - fn warn_unknown_profile(&mut self, cx: &LateContext<'_>, entry: &ProfileEntry) { - if self.warned_unknown_profiles.insert(entry.span) { - let attr_name = if entry.attr_name == sym::disallowed_profiles { - "clippy::disallowed_profiles" - } else { - "clippy::disallowed_profile" - }; - span_lint( - cx, - DISALLOWED_METHODS, - entry.span, - format!( - "`{attr_name}` references unknown profile `{}` for `clippy::disallowed_methods`", - entry.name - ), - ); - } + Self { disallowed } } } @@ -181,43 +98,13 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods { }, _ => return, }; - let mut active_profiles = SmallVec::<[Symbol; 2]>::new(); - // Copy entries out of the cache before iterating: `warn_unknown_profile` takes - // `&mut self`, which conflicts with the borrow held by `active_profiles(...)`. - let entries: SmallVec<[ProfileEntry; 2]> = self - .profile_cache - .active_profiles(cx, expr.hir_id) - .map(|selection| selection.iter().copied().collect()) - .unwrap_or_default(); - for entry in &entries { - if self.profiles.contains_key(&entry.name) { - active_profiles.push(entry.name); - } else if !self.known_profiles.contains(&entry.name) { - self.warn_unknown_profile(cx, entry); - } - } - - if let Some((profile, &(path, disallowed_path))) = active_profiles.iter().find_map(|symbol| { - self.profiles - .get(symbol) - .and_then(|map| map.get(&id).map(|info| (*symbol, info))) - }) { - let diag_amendment = disallowed_path.diag_amendment(span); - span_lint_and_then( - cx, - DISALLOWED_METHODS, - span, - format!("use of a disallowed method `{path}` (profile: {profile})"), - |diag| diag_amendment(diag), - ); - } else if let Some(&(path, disallowed_path)) = self.default.get(&id) { - let diag_amendment = disallowed_path.diag_amendment(span); + if let Some(&(path, disallowed_path)) = self.disallowed.get(&id) { span_lint_and_then( cx, DISALLOWED_METHODS, span, format!("use of a disallowed method `{path}`"), - |diag| diag_amendment(diag), + disallowed_path.diag_amendment(span), ); } } diff --git a/src/tools/clippy/clippy_lints/src/disallowed_types.rs b/src/tools/clippy/clippy_lints/src/disallowed_types.rs index 37dd605617a7a..2c520d053f439 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_types.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_types.rs @@ -1,18 +1,15 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::disallowed_profiles::{ProfileEntry, ProfileResolver}; +use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::paths::PathNS; -use clippy_utils::sym; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_data_structures::smallvec::SmallVec; +use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{AmbigArg, Item, ItemKind, PolyTraitRef, PrimTy, Ty, TyKind, UseKind}; use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty::TyCtxt; use rustc_session::impl_lint_pass; -use rustc_span::{Span, Symbol}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -54,17 +51,6 @@ declare_clippy_lint! { /// // A similar type that is allowed by the config /// use std::collections::HashMap; /// ``` - /// - /// Disallowed profiles can scope lists to specific modules: - /// ```toml - /// [profiles.forward_pass] - /// disallowed-types = [{ path = "crate::buffers::HostBuffer", reason = "Prefer device buffers in forward computations" }] - /// ``` - /// - /// ```rust,ignore - /// #[clippy::disallowed_profile("forward_pass")] - /// fn forward_step(buffer: crate::buffers::DeviceBuffer) { /* ... */ } - /// ``` #[clippy::version = "1.55.0"] pub DISALLOWED_TYPES, style, @@ -73,127 +59,37 @@ declare_clippy_lint! { impl_lint_pass!(DisallowedTypes => [DISALLOWED_TYPES]); -struct TypeLookup { +pub struct DisallowedTypes { def_ids: DefIdMap<(&'static str, &'static DisallowedPath)>, prim_tys: FxHashMap, } -impl TypeLookup { - fn from_config(tcx: TyCtxt<'_>, paths: &'static [DisallowedPath]) -> Self { - let (def_ids, prim_tys) = create_disallowed_map(tcx, paths, PathNS::Type, def_kind_predicate, "type", true); - Self { def_ids, prim_tys } - } - - fn find(&self, res: &Res) -> Option<(&'static str, &'static DisallowedPath)> { - match res { - Res::Def(_, did) => self.def_ids.get(did).copied(), - Res::PrimTy(prim) => self.prim_tys.get(prim).copied(), - _ => None, - } - } -} - -pub struct DisallowedTypes { - default: TypeLookup, - /// Lookup per profile that declares a non-empty `disallowed_types` list. Profiles - /// declared in `[profiles.*]` but without `disallowed_types` entries are absent here. - profiles: FxHashMap, - /// Every profile name declared in `[profiles.*]`, regardless of whether it contributes - /// to this lint. Used to suppress the "unknown profile" warning for profiles that exist - /// in config but only define entries for other lints (e.g. `disallowed_methods`). - known_profiles: FxHashSet, - profile_cache: ProfileResolver, - warned_unknown_profiles: FxHashSet, -} - impl DisallowedTypes { - #[allow(rustc::potential_query_instability)] // Profiles are sorted for deterministic iteration. pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self { - let default = TypeLookup::from_config(tcx, &conf.disallowed_types); - - let mut profiles = FxHashMap::default(); - let mut known_profiles = FxHashSet::default(); - let mut profile_entries: Vec<_> = conf.profiles.iter().collect(); - profile_entries.sort_by_key(|(a, _)| *a); - for (name, profile) in profile_entries { - let symbol = Symbol::intern(name.as_str()); - known_profiles.insert(symbol); - - let paths = profile.disallowed_types.as_slice(); - if paths.is_empty() { - continue; - } - profiles.insert(symbol, TypeLookup::from_config(tcx, paths)); - } - - Self { - default, - profiles, - known_profiles, - profile_cache: ProfileResolver::default(), - warned_unknown_profiles: FxHashSet::default(), - } - } - - fn warn_unknown_profile(&mut self, cx: &LateContext<'_>, entry: &ProfileEntry) { - if self.warned_unknown_profiles.insert(entry.span) { - let attr_name = if entry.attr_name == sym::disallowed_profiles { - "clippy::disallowed_profiles" - } else { - "clippy::disallowed_profile" - }; - span_lint( - cx, - DISALLOWED_TYPES, - entry.span, - format!( - "`{attr_name}` references unknown profile `{}` for `clippy::disallowed_types`", - entry.name - ), - ); - } + let (def_ids, prim_tys) = create_disallowed_map( + tcx, + &conf.disallowed_types, + PathNS::Type, + def_kind_predicate, + "type", + true, + ); + Self { def_ids, prim_tys } } - fn check_res_emit(&mut self, cx: &LateContext<'_>, hir_id: rustc_hir::HirId, res: &Res, span: Span) { - let mut active_profiles = SmallVec::<[Symbol; 2]>::new(); - // Copy entries out of the cache before iterating: `warn_unknown_profile` takes - // `&mut self`, which conflicts with the borrow held by `active_profiles(...)`. - let entries: SmallVec<[ProfileEntry; 2]> = self - .profile_cache - .active_profiles(cx, hir_id) - .map(|selection| selection.iter().copied().collect()) - .unwrap_or_default(); - for entry in &entries { - if self.profiles.contains_key(&entry.name) { - active_profiles.push(entry.name); - } else if !self.known_profiles.contains(&entry.name) { - self.warn_unknown_profile(cx, entry); - } - } - - if let Some((profile, (path, disallowed_path))) = active_profiles.iter().find_map(|symbol| { - self.profiles - .get(symbol) - .and_then(|lookup| lookup.find(res).map(|info| (*symbol, info))) - }) { - let diag_amendment = disallowed_path.diag_amendment(span); - span_lint_and_then( - cx, - DISALLOWED_TYPES, - span, - format!("use of a disallowed type `{path}` (profile: {profile})"), - |diag| diag_amendment(diag), - ); - } else if let Some((path, disallowed_path)) = self.default.find(res) { - let diag_amendment = disallowed_path.diag_amendment(span); - span_lint_and_then( - cx, - DISALLOWED_TYPES, - span, - format!("use of a disallowed type `{path}`"), - |diag| diag_amendment(diag), - ); - } + fn check_res_emit(&self, cx: &LateContext<'_>, res: &Res, span: Span) { + let (path, disallowed_path) = match res { + Res::Def(_, did) if let Some(&x) = self.def_ids.get(did) => x, + Res::PrimTy(prim) if let Some(&x) = self.prim_tys.get(prim) => x, + _ => return, + }; + span_lint_and_then( + cx, + DISALLOWED_TYPES, + span, + format!("use of a disallowed type `{path}`"), + disallowed_path.diag_amendment(span), + ); } } @@ -215,22 +111,17 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes { if let ItemKind::Use(path, UseKind::Single(_)) = &item.kind && let Some(res) = path.res.type_ns { - self.check_res_emit(cx, item.hir_id(), &res, item.span); + self.check_res_emit(cx, &res, item.span); } } fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx, AmbigArg>) { if let TyKind::Path(path) = &ty.kind { - self.check_res_emit(cx, ty.hir_id, &cx.qpath_res(path, ty.hir_id), ty.span); + self.check_res_emit(cx, &cx.qpath_res(path, ty.hir_id), ty.span); } } fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>) { - self.check_res_emit( - cx, - poly.trait_ref.hir_ref_id, - &poly.trait_ref.path.res, - poly.trait_ref.path.span, - ); + self.check_res_emit(cx, &poly.trait_ref.path.res, poly.trait_ref.path.span); } } diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs index b164a9a99782b..4139df9cd7997 100644 --- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs +++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs @@ -99,7 +99,7 @@ pub fn check( fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option { let mut panic_span = None; let typeck = cx.tcx.typeck_body(body_id); - for_each_expr(cx, cx.tcx.hir_body(body_id), |expr| { + for_each_expr(cx.tcx, cx.tcx.hir_body(body_id), |expr| { if is_inside_always_const_context(cx.tcx, expr.hir_id) { return ControlFlow::::Continue(()); } diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs index 81da571bdd601..c5816cb8b2d63 100644 --- a/src/tools/clippy/clippy_lints/src/doc/mod.rs +++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs @@ -1124,6 +1124,9 @@ fn check_doc<'a, Events: Iterator, Range, Range, Range Gap<'a> { let prev_stop = prev_chunk.last()?; let next_stop = next_chunk.first()?; let gap_span = prev_stop.span.between(next_stop.span); - let gap_snippet = gap_span.get_source_text(cx)?; + let gap_snippet = gap_span.get_text(cx)?; let mut has_comment = false; let mut empty_lines = Vec::new(); diff --git a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs index ee1be48417222..b01745008510c 100644 --- a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs +++ b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs @@ -1,6 +1,6 @@ use clippy_utils::attrs::span_contains_cfg; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::span_contains_non_whitespace; use rustc_data_structures::fx::{FxIndexMap, IndexEntry}; use rustc_errors::Applicability; @@ -192,7 +192,7 @@ impl LateLintPass<'_> for EmptyWithBrackets { // Span of the parentheses in variant definition let span = variant.span.with_lo(variant.ident.span.hi()); let span_inner = span - .with_lo(SpanRangeExt::trim_start(span, cx).start + BytePos(1)) + .with_lo(SpanExt::trim_start(span, cx).start + BytePos(1)) .with_hi(span.hi() - BytePos(1)); if span_contains_non_whitespace(cx, span_inner, false) { continue; diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs index a24cc957df070..cba40abf04e17 100644 --- a/src/tools/clippy/clippy_lints/src/entry.rs +++ b/src/tools/clippy/clippy_lints/src/entry.rs @@ -604,7 +604,7 @@ fn is_any_expr_in_map_used<'tcx>( map: &'tcx Expr<'tcx>, expr: &'tcx Expr<'tcx>, ) -> bool { - for_each_expr(cx, map, |e| { + for_each_expr(cx.tcx, map, |e| { if spanless_eq.eq_expr(ctxt, e, expr) { return ControlFlow::Break(()); } diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs index 93a6dc59993a6..a6f80da5dab9c 100644 --- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs +++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs @@ -102,7 +102,7 @@ impl<'cx, 'tcx> TypeWalker<'cx, 'tcx> { fn emit_sugg(&self, spans: Vec, msg: String, help: &'static str) { let suggestions: Vec<(Span, String)> = spans.iter().copied().zip(std::iter::repeat(String::new())).collect(); span_lint_and_then(self.cx, EXTRA_UNUSED_TYPE_PARAMETERS, spans, msg, |diag| { - diag.multipart_suggestion(help, suggestions, Applicability::MachineApplicable); + diag.multipart_suggestion(help, suggestions, Applicability::MaybeIncorrect); }); } diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic/mul_add.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic/mul_add.rs index 888d5b7b762a0..eb96e65638445 100644 --- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic/mul_add.rs +++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic/mul_add.rs @@ -91,7 +91,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { cx, SUBOPTIMAL_FLOPS, expr.span, - "multiply and add expressions can be calculated more efficiently and accurately", + "multiply and add expressions may be calculated more efficiently and accurately", |diag| { let maybe_neg_sugg = |expr, app: &mut _| { let sugg = Sugg::hir_with_applicability(cx, expr, "_", app); @@ -120,6 +120,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) { }, app, ); + diag.note_once("the performance gain from `mul_add` may vary depending on the target architecture"); }, ); } diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs index 2691fb4766397..f13278d91ce7f 100644 --- a/src/tools/clippy/clippy_lints/src/format.rs +++ b/src/tools/clippy/clippy_lints/src/format.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::macros::{FormatArgsStorage, find_format_arg_expr, first_node_in_macro, matching_root_macro_call}; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::sugg::Sugg; use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait}; use rustc_errors::Applicability; @@ -69,7 +69,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat { ([], []) => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability), ([], [_]) => { // Simulate macro expansion, converting {{ and }} to { and }. - let Some(snippet) = format_args.span.get_source_text(cx) else { + let Some(snippet) = format_args.span.get_text(cx) else { return; }; let s_expand = snippet.replace("{{", "{").replace("}}", "}"); diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 05145456071c0..9014302fb8ea6 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -10,7 +10,7 @@ use clippy_utils::macros::{ }; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_opt}; +use clippy_utils::source::{SpanExt, snippet, snippet_opt}; use clippy_utils::ty::implements_trait; use clippy_utils::{is_from_proc_macro, is_in_test, peel_hir_expr_while, sym, trait_ref_of_method}; use itertools::Itertools; @@ -380,7 +380,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { /// Check if there is a comma after the last format macro arg. fn check_trailing_comma(&self) { let span = self.macro_call.span; - if let Some(src) = span.get_source_text(self.cx) + if let Some(src) = span.get_text(self.cx) && let Some(src) = src.strip_suffix([')', ']', '}']) && let src = src.trim_end_matches(|c: char| c.is_whitespace() && c != '\n') && let Some(src) = src.strip_suffix(',') @@ -694,7 +694,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> { count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter()) && implements_trait(cx, target, display_trait_id, &[]) && let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait() - && let Some(receiver_snippet) = receiver.span.source_callsite().get_source_text(cx) + && let Some(receiver_snippet) = receiver.span.source_callsite().get_text(cx) { let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]); if n_needed_derefs == 0 && !needs_ref { diff --git a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs index 57c1dd2eb72b0..c64e742592080 100644 --- a/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs +++ b/src/tools/clippy/clippy_lints/src/four_forward_slashes.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt as _; +use clippy_utils::source::SpanExt as _; use itertools::Itertools; use rustc_errors::Applicability; use rustc_hir::Item; @@ -86,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for FourForwardSlashes { // If the comment contains a bare CR (not followed by a LF), do not propose an auto-fix // as bare CR are not allowed in doc comments. - if span.check_source_text(cx, contains_bare_cr) { + if span.check_text(cx, contains_bare_cr) { diag.help(msg) .note("bare CR characters are not allowed in doc comments"); return; diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs index 433d591418798..fb09cac1fe702 100644 --- a/src/tools/clippy/clippy_lints/src/from_over_into.rs +++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::span_is_local; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::intravisit::{Visitor, walk_path}; use rustc_hir::{ @@ -197,8 +197,8 @@ fn convert_to_from( return None; }; - let from = self_ty.span.get_source_text(cx)?; - let into = target_ty.span.get_source_text(cx)?; + let from = self_ty.span.get_text(cx)?; + let into = target_ty.span.get_text(cx)?; let mut suggestions = vec![ // impl Into for U -> impl From for U diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs index 39b53e02831c4..1dc23df6706ca 100644 --- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs +++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs @@ -163,12 +163,11 @@ fn check_needless_must_use( ); } else if reason.is_none() && is_must_use_ty(cx, return_ty(cx, item_id)) { // Ignore async functions unless Future::Output type is a must_use type - if sig.header.is_async() { - if let Some(future_ty) = cx.tcx.get_impl_future_output_ty(return_ty(cx, item_id)) - && !is_must_use_ty(cx, future_ty) - { - return; - } + if sig.header.is_async() + && let Some(future_ty) = cx.tcx.get_impl_future_output_ty(return_ty(cx, item_id)) + && !is_must_use_ty(cx, future_ty) + { + return; } span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs index e49dee4164b88..f3952b2687a56 100644 --- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs +++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs @@ -50,7 +50,7 @@ fn check_raw_ptr<'tcx>( if !raw_ptrs.is_empty() { let typeck = cx.tcx.typeck_body(body.id()); - let _: Option = for_each_expr(cx, body.value, |e| { + let _: Option = for_each_expr(cx.tcx, body.value, |e| { match e.kind { hir::ExprKind::Call(f, args) if is_unsafe_fn(cx, typeck.expr_ty(f)) => { for arg in args { diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs index 33eede8e65ac9..a5fed9f03a620 100644 --- a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs +++ b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; @@ -23,7 +23,7 @@ pub(super) fn check_fn( } let mut line_count: u64 = 0; - let too_many = body.value.span.check_source_text(cx, |src| { + let too_many = body.value.span.check_text(cx, |src| { let mut in_comment = false; let mut code_in_line; diff --git a/src/tools/clippy/clippy_lints/src/if_not_else.rs b/src/tools/clippy/clippy_lints/src/if_not_else.rs index ff22ba4fcd0d6..ea1992d471312 100644 --- a/src/tools/clippy/clippy_lints/src/if_not_else.rs +++ b/src/tools/clippy/clippy_lints/src/if_not_else.rs @@ -1,7 +1,7 @@ use clippy_utils::consts::is_zero_integer_const; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::is_else_clause; -use clippy_utils::source::{HasSession, indent_of, reindent_multiline, snippet_with_context}; +use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_context}; use rustc_errors::Applicability; use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp}; use rustc_lint::{LateContext, LateLintPass}; @@ -104,28 +104,27 @@ impl LateLintPass<'_> for IfNotElse { } } -fn make_sugg<'a>( - sess: &impl HasSession, +fn make_sugg( + cx: &LateContext<'_>, expr_span: Span, - cond_kind: &'a ExprKind<'a>, + cond_kind: &ExprKind<'_>, cond_inner: Span, els_span: Span, - default: &'a str, + default: &str, applicability: &mut Applicability, ) -> String { - let (cond_inner_snip, _) = snippet_with_context(sess, cond_inner, expr_span.ctxt(), default, applicability); - let (els_snip, _) = snippet_with_context(sess, els_span, expr_span.ctxt(), default, applicability); - let indent = indent_of(sess, expr_span); - + let (cond_inner_snip, _) = snippet_with_context(cx, cond_inner, expr_span.ctxt(), default, applicability); + let (els_snip, _) = snippet_with_context(cx, els_span, expr_span.ctxt(), default, applicability); + let indent = indent_of(cx, expr_span); let suggestion = match cond_kind { ExprKind::Unary(UnOp::Not, cond_rest) => { let (cond_rest_snip, _) = - snippet_with_context(sess, cond_rest.span, expr_span.ctxt(), default, applicability); + snippet_with_context(cx, cond_rest.span, expr_span.ctxt(), default, applicability); format!("if {cond_rest_snip} {els_snip} else {cond_inner_snip}") }, ExprKind::Binary(_, lhs, rhs) => { - let (lhs_snip, _) = snippet_with_context(sess, lhs.span, expr_span.ctxt(), default, applicability); - let (rhs_snip, _) = snippet_with_context(sess, rhs.span, expr_span.ctxt(), default, applicability); + let (lhs_snip, _) = snippet_with_context(cx, lhs.span, expr_span.ctxt(), default, applicability); + let (rhs_snip, _) = snippet_with_context(cx, rhs.span, expr_span.ctxt(), default, applicability); format!("if {lhs_snip} == {rhs_snip} {els_snip} else {cond_inner_snip}") }, diff --git a/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs b/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs index 06ebd3ac7f5fd..2701cdaa394da 100644 --- a/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs +++ b/src/tools/clippy/clippy_lints/src/ifs/branches_sharing_code.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::{IntoSpan, SpanRangeExt, first_line_of_span, indent_of, reindent_multiline, snippet}; +use clippy_utils::source::{IntoSpan, SpanExt, first_line_of_span, indent_of, reindent_multiline, snippet}; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{ diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs index 70176c62772b7..bf2c7a007644c 100644 --- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs +++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs @@ -13,7 +13,7 @@ use rustc_session::declare_lint_pass; use rustc_span::Span; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet, snippet_with_context}; +use clippy_utils::source::{IntoSpan, SpanExt, snippet, snippet_with_context}; use clippy_utils::sym; declare_clippy_lint! { diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs index 1ae8198a432dd..99e7d202fe46f 100644 --- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs +++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs @@ -193,10 +193,11 @@ impl IncompatibleMsrv { } } - if (self.check_in_tests || !is_in_test(cx.tcx, node)) - && let Some(current) = self.msrv.current(cx) + // Check `is_in_test` last as it walks the HIR parent chain. + if let Some(current) = self.msrv.current(cx) && let Availability::Since(version) = self.get_def_id_availability(cx.tcx, def_id, needs_const) && version > current + && (self.check_in_tests || !is_in_test(cx.tcx, node)) { span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs index bc57d9e85478a..aae847031bf64 100644 --- a/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs +++ b/src/tools/clippy/clippy_lints/src/ineffective_open_options.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{peel_blocks, peel_hir_expr_while, sym}; use rustc_ast::LitKind; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs b/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs index 3ffa23dcd6046..87e63e8732fe8 100644 --- a/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/inline_trait_bounds.rs @@ -1,11 +1,11 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{HasSession, snippet}; +use clippy_utils::source::snippet; use clippy_utils::sym; use rustc_ast::ast::{Fn, FnRetTy, GenericParam, GenericParamKind}; use rustc_ast::visit::{FnCtxt, FnKind}; use rustc_ast::{HasAttrs as _, NodeId}; use rustc_errors::Applicability; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::Span; diff --git a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs index dd63de288b87f..f83bcb3cf4076 100644 --- a/src/tools/clippy/clippy_lints/src/items_after_test_module.rs +++ b/src/tools/clippy/clippy_lints/src/items_after_test_module.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{fulfill_or_allowed, is_cfg_test, is_from_proc_macro}; use rustc_errors::{Applicability, SuggestionStyle}; use rustc_hir::{HirId, Item, ItemKind, Mod}; @@ -99,7 +99,7 @@ impl LateLintPass<'_> for ItemsAfterTestModule { if let Some(prev) = mod_pos.checked_sub(1) && let prev = cx.tcx.hir_item(module.item_ids[prev]) && let items_span = last.span.with_lo(test_mod.span.hi()) - && let Some(items) = items_span.get_source_text(cx) + && let Some(items) = items_span.get_text(cx) { diag.multipart_suggestion_with_style( "move the items to before the test module was defined", diff --git a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs index 7cf594384b5a2..dec3f3bcae895 100644 --- a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs +++ b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs @@ -2,7 +2,7 @@ use std::{fmt, ops}; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{HasSession, SpanRangeExt}; +use clippy_utils::source::SpanExt; use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_in_test}; use rustc_errors::Diag; use rustc_hir::def_id::LocalDefId; @@ -197,7 +197,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames { // TODO: Is there a cleaner, robust way to ask this question? // The obvious `LocalDecl::is_user_variable()` panics on "unwrapping cross-crate data", // and that doesn't get us the true name in scope rather than the span text either. - if let Some(name) = local_span.get_source_text(cx) + if let Some(name) = local_span.get_text(cx) && is_ident(&name) { // If the local is an ordinary named variable, @@ -228,11 +228,11 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames { if fn_span.from_expansion() { // Don't lint on the main function generated by `--test` target - if cx.sess().is_test_crate() && is_entrypoint_fn(cx, local_def_id.to_def_id()) { + if cx.tcx.sess.is_test_crate() && is_entrypoint_fn(cx, local_def_id.to_def_id()) { return; } - let is_from_external_macro = fn_span.in_external_macro(cx.sess().source_map()); + let is_from_external_macro = fn_span.in_external_macro(cx.tcx.sess.source_map()); span_lint_and_then( cx, LARGE_STACK_FRAMES, diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs index 838abc81ce449..48e1414defadf 100644 --- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs +++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{is_from_proc_macro, sym}; use hir::def_id::DefId; use rustc_errors::Applicability; @@ -121,7 +121,7 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants { && let QPath::TypeRelative(ty, last_segment) = qpath && let Some(def_id) = cx.qpath_res(qpath, func.hir_id).opt_def_id() && is_integer_method(cx, def_id) - && let Some(mod_name) = ty.span.get_source_text(cx) + && let Some(mod_name) = ty.span.get_text(cx) && ty.span.eq_ctxt(last_segment.ident.span) { let name = last_segment.ident.name.as_str()[..=2].to_ascii_uppercase(); diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs index 53ee157dd6d70..a84db8929713e 100644 --- a/src/tools/clippy/clippy_lints/src/len_zero.rs +++ b/src/tools/clippy/clippy_lints/src/len_zero.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::ty::implements_trait; use clippy_utils::{parent_item_name, peel_ref_operators, sym}; @@ -261,7 +261,7 @@ impl LenZero { } fn span_without_enclosing_paren(cx: &LateContext<'_>, span: Span) -> Span { - let Some(snippet) = span.get_source_text(cx) else { + let Some(snippet) = span.get_text(cx) else { return span; }; if has_enclosing_paren(snippet) { diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs index 984574c221fb6..3f8bd2223876f 100644 --- a/src/tools/clippy/clippy_lints/src/let_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs @@ -162,7 +162,8 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore { ); }, ); - } else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() + } else if local.ty.is_none() + && let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait() && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[]) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs index 7f2687664c686..3f4b59382ca15 100644 --- a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs +++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use rustc_ast::{Local, TyKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index a0a5558b42eac..d002df267027d 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -454,9 +454,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co // NOTE: Do not add any more pre-expansion passes. These should be removed eventually. // Due to the architecture of the compiler, currently `cfg_attr` attributes on crate // level (i.e `#![cfg_attr(...)]`) will still be expanded even when using a pre-expansion pass. - store.register_pre_expansion_pass( - Box::new(move || Box::new(attrs::EarlyAttributes::new(conf))) - ); + store.register_pre_expansion_pass(Box::new(move || Box::new(attrs::EarlyAttributes::new(conf)))); let format_args_storage = FormatArgsStorage::default(); let attr_storage = AttrStorage::default(); @@ -531,7 +529,6 @@ rustc_lint::early_lint_methods!( PartialPubFields: partial_pub_fields::PartialPubFields = partial_pub_fields::PartialPubFields, UnderscoreTyped: let_with_type_underscore::UnderscoreTyped = let_with_type_underscore::UnderscoreTyped, ExcessiveNesting: excessive_nesting::ExcessiveNesting = excessive_nesting::ExcessiveNesting::new(conf), - RefPatterns: ref_patterns::RefPatterns = ref_patterns::RefPatterns, NeedlessElse: needless_else::NeedlessElse = needless_else::NeedlessElse, RawStrings: raw_strings::RawStrings = raw_strings::RawStrings::new(conf), Visibility: visibility::Visibility = visibility::Visibility, @@ -729,7 +726,7 @@ rustc_lint::late_lint_methods!( UndocumentedUnsafeBlocks: undocumented_unsafe_blocks::UndocumentedUnsafeBlocks = undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::new(conf), FormatArgs: format_args::FormatArgs<'tcx> = format_args::FormatArgs::new(tcx, conf, format_args.clone()), TrailingEmptyArray: trailing_empty_array::TrailingEmptyArray = trailing_empty_array::TrailingEmptyArray, - NeedlessLateInit: needless_late_init::NeedlessLateInit = needless_late_init::NeedlessLateInit, + NeedlessLateInit: needless_late_init::NeedlessLateInit<'tcx> = needless_late_init::NeedlessLateInit::new(conf), ReturnSelfNotMustUse: return_self_not_must_use::ReturnSelfNotMustUse = return_self_not_must_use::ReturnSelfNotMustUse, NumberedFields: init_numbered_fields::NumberedFields = init_numbered_fields::NumberedFields, ManualBits: manual_bits::ManualBits = manual_bits::ManualBits::new(conf), @@ -859,6 +856,7 @@ rustc_lint::late_lint_methods!( ByteCharSlice: byte_char_slices::ByteCharSlice = byte_char_slices::ByteCharSlice, ManualAssertEq: manual_assert_eq::ManualAssertEq = manual_assert_eq::ManualAssertEq, WithCapacityZero: with_capacity_zero::WithCapacityZero = with_capacity_zero::WithCapacityZero, + RefPatterns: ref_patterns::RefPatterns = ref_patterns::RefPatterns, // add late passes here, used by `cargo dev new_lint` ]] ); diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs index 2b4828872b202..3e58448af5266 100644 --- a/src/tools/clippy/clippy_lints/src/lifetimes.rs +++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs @@ -161,7 +161,9 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes { } fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly_trait_ref: &'tcx PolyTraitRef<'tcx>) { - report_extra_trait_object_lifetimes(cx, poly_trait_ref.bound_generic_params, &poly_trait_ref.trait_ref); + if !poly_trait_ref.span.from_expansion() { + report_extra_trait_object_lifetimes(cx, poly_trait_ref.bound_generic_params, &poly_trait_ref.trait_ref); + } } fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs index 2ec046c8d6ba4..4e7ff879ae590 100644 --- a/src/tools/clippy/clippy_lints/src/literal_representation.rs +++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::numeric_literal::{NumericLiteral, Radix}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::ast::{Expr, ExprKind, LitKind}; use rustc_ast::token; use rustc_errors::Applicability; @@ -207,7 +207,9 @@ pub struct LiteralDigitGrouping { impl EarlyLintPass for LiteralDigitGrouping { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + // `NumericLiteral::from_lit_kind` only accepts integer and float literals. if let ExprKind::Lit(lit) = expr.kind + && matches!(lit.kind, token::LitKind::Integer | token::LitKind::Float) && !expr.span.in_external_macro(cx.sess().source_map()) { self.check_lit(cx, lit, expr.span); @@ -226,7 +228,7 @@ impl LiteralDigitGrouping { } fn check_lit(&self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) { - if let Some(src) = span.get_source_text(cx) + if let Some(src) = span.get_text(cx) && let Ok(lit_kind) = LitKind::from_token_lit(lit) && let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) { @@ -418,7 +420,9 @@ pub struct DecimalLiteralRepresentation { impl EarlyLintPass for DecimalLiteralRepresentation { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { + // Only integer tokens can produce `LitKind::Int`. if let ExprKind::Lit(lit) = expr.kind + && lit.kind == token::LitKind::Integer && !expr.span.in_external_macro(cx.sess().source_map()) { self.check_lit(cx, lit, expr.span); @@ -436,10 +440,10 @@ impl DecimalLiteralRepresentation { // Lint integral literals. if let Ok(lit_kind) = LitKind::from_token_lit(lit) && let LitKind::Int(val, _) = lit_kind - && let Some(src) = span.get_source_text(cx) + && val >= u128::from(self.threshold) + && let Some(src) = span.get_text(cx) && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind) && num_lit.radix == Radix::Decimal - && val >= u128::from(self.threshold) { let hex = format!("{val:#X}"); let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false); diff --git a/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs b/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs index 41022e6a5321b..f4d71d0524b22 100644 --- a/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs +++ b/src/tools/clippy/clippy_lints/src/loops/char_indices_as_byte_indices.rs @@ -48,7 +48,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr && let PatKind::Binding(_, binding_id, ..) = pat.kind { // Destructured iterator element `(idx, _)`, look for uses of the binding - for_each_expr(cx, body, |expr| { + for_each_expr(cx.tcx, body, |expr| { if expr.res_local_id() == Some(binding_id) { check_index_usage(cx, expr, pat, enumerate_span, chars_span, chars_recv); } @@ -56,7 +56,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, iterable: &Expr }); } else if let PatKind::Binding(_, binding_id, ..) = pat.kind { // Bound as a tuple, look for `tup.0` - for_each_expr(cx, body, |expr| { + for_each_expr(cx.tcx, body, |expr| { if let ExprKind::Field(e, field) = expr.kind && e.res_local_id() == Some(binding_id) && field.name == sym::integer(0) diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs index ffc6f7186922a..ceaf681088399 100644 --- a/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs +++ b/src/tools/clippy/clippy_lints/src/loops/manual_slice_fill.rs @@ -13,6 +13,7 @@ use rustc_hir::QPath::Resolved; use rustc_hir::def::Res; use rustc_hir::{Expr, ExprKind, Pat}; use rustc_lint::LateContext; +use rustc_middle::ty; use rustc_span::{Spanned, sym}; use super::MANUAL_SLICE_FILL; @@ -84,6 +85,24 @@ pub(super) fn check<'tcx>( { sugg(cx, body, expr, recv_path.span, assignval.span); } + // `for slot in s { *slot = value; }` where `s` is already `&mut [T; N]` + else if let ExprKind::Assign(assignee, assignval, _) = peel_blocks_with_stmt(body).kind + && let ExprKind::Unary(UnOp::Deref, slice_iter) = assignee.kind + && let ExprKind::Path(Resolved(_, slice_path)) = slice_iter.kind + && let Res::Local(local) = slice_path.res + && local == pat.hir_id + && !assignval.span.from_expansion() + && switch_to_eager_eval(cx, assignval) + && !is_local_used(cx, assignval, local) + && let arg_ty = cx.typeck_results().expr_ty(arg) + && let ty::Ref(_, inner_ty, rustc_ast::Mutability::Mut) = arg_ty.kind() + && is_slice_like(cx, *inner_ty) + && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() + && implements_trait(cx, *inner_ty, clone_trait, &[]) + && msrv.meets(cx, msrvs::SLICE_FILL) + { + sugg(cx, body, expr, arg.span, assignval.span); + } } fn sugg<'tcx>( diff --git a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs index 4ecfadbf9d494..b80efdae9294c 100644 --- a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs +++ b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs @@ -1,7 +1,7 @@ use super::UNUSED_ENUMERATE_INDEX; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, walk_span_to_context}; +use clippy_utils::source::{SpanExt, walk_span_to_context}; use clippy_utils::{expr_or_init, pat_is_wild, sym}; use rustc_errors::Applicability; use rustc_hir::{Closure, Expr, ExprKind, Pat, PatKind, TyKind}; diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs index bee3b19b597c0..161834914b309 100644 --- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs +++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, position_before_rarrow, snippet_block}; +use clippy_utils::source::{SpanExt, position_before_rarrow, snippet_block}; use rustc_errors::Applicability; use rustc_hir::intravisit::FnKind; use rustc_hir::{ @@ -76,8 +76,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn { "this function can be simplified using the `async fn` syntax", |diag| { if let Some(vis_span) = vis_span_opt - && let Some(vis_snip) = vis_span.get_source_text(cx) - && let Some(header_snip) = header_span.get_source_text(cx) + && let Some(vis_snip) = vis_span.get_text(cx) + && let Some(header_snip) = header_span.get_text(cx) && let Some(ret_pos) = position_before_rarrow(&header_snip) && let Some((_, ret_snip)) = suggested_ret(cx, output) { @@ -185,6 +185,6 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str, Some((sugg, String::new())) } else { let sugg = "return the output of the future directly"; - output.span.get_source_text(cx).map(|src| (sugg, format!(" -> {src}"))) + output.span.get_text(cx).map(|src| (sugg, format!(" -> {src}"))) } } diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs index 6848af7748f3f..c81fa2e2b3b27 100644 --- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs +++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs @@ -140,7 +140,13 @@ struct InputMinMax<'tcx> { impl<'tcx> LateLintPass<'tcx> for ManualClamp { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { - if !expr.span.from_expansion() && !is_in_const_context(cx) { + // Cheap kind check before the costlier const context query. + if matches!( + expr.kind, + ExprKind::If(..) | ExprKind::Match(..) | ExprKind::MethodCall(..) | ExprKind::Call(..) + ) && !expr.span.from_expansion() + && !is_in_const_context(cx) + { let suggestion = is_if_elseif_else_pattern(cx, expr) .or_else(|| is_max_min_pattern(cx, expr)) .or_else(|| is_call_max_min_pattern(cx, expr)) @@ -155,6 +161,15 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp { } fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + // Cheap `if`-statement check before the costlier const context query. + if !block + .stmts + .iter() + .any(|stmt| matches!(stmt.kind, StmtKind::Expr(e) if matches!(e.kind, ExprKind::If(..)))) + { + return; + } + if is_in_const_context(cx) || !self.msrv.meets(cx, msrvs::CLAMP) { return; } @@ -293,18 +308,19 @@ fn is_if_elseif_else_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx /// ``` fn is_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option> { if let ExprKind::MethodCall(seg_second, receiver, [arg_second], _) = expr.kind + && let ExprKind::MethodCall(seg_first, input, [arg_first], _) = &receiver.kind + // Match method names before the costlier type queries. + && let Some((min, max)) = match (seg_first.ident.name, seg_second.ident.name) { + (sym::min, sym::max) => Some((arg_second, arg_first)), + (sym::max, sym::min) => Some((arg_first, arg_second)), + _ => None, + } && (cx.typeck_results().expr_ty_adjusted(receiver).is_floating_point() || cx.ty_based_def(expr).assoc_fn_parent(cx).is_diag_item(cx, sym::Ord)) - && let ExprKind::MethodCall(seg_first, input, [arg_first], _) = &receiver.kind && (cx.typeck_results().expr_ty_adjusted(input).is_floating_point() || cx.ty_based_def(receiver).assoc_fn_parent(cx).is_diag_item(cx, sym::Ord)) { let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); - let (min, max) = match (seg_first.ident.name, seg_second.ident.name) { - (sym::min, sym::max) => (arg_second, arg_first), - (sym::max, sym::min) => (arg_first, arg_second), - _ => return None, - }; Some(ClampSuggestion { params: InputMinMax { input, @@ -367,12 +383,16 @@ fn is_call_max_min_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) && let Some(inner_seg) = segment(cx, inner_fn) && let Some(outer_seg) = segment(cx, outer_fn) { - let (input, inner_arg) = match (is_const_evaluatable(cx, first), is_const_evaluatable(cx, second)) { + let typeck = cx.typeck_results(); + let (input, inner_arg) = match ( + is_const_evaluatable(cx.tcx, typeck, first), + is_const_evaluatable(cx.tcx, typeck, second), + ) { (true, false) => (second, first), (false, true) => (first, second), _ => return None, }; - let is_float = cx.typeck_results().expr_ty_adjusted(input).is_floating_point(); + let is_float = typeck.expr_ty_adjusted(input).is_floating_point(); let (min, max) = match (inner_seg, outer_seg) { (FunctionType::CmpMin, FunctionType::CmpMax) => (outer_arg, inner_arg), (FunctionType::CmpMax, FunctionType::CmpMin) => (inner_arg, outer_arg), diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs index 4a2784caf4c74..acd47fcd279db 100644 --- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs +++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs @@ -4,7 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; @@ -157,7 +157,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods { // case somebody does that for some reason && (const_1.is_pos_infinity() && const_2.is_neg_infinity() || const_1.is_neg_infinity() && const_2.is_pos_infinity()) - && let Some(local_snippet) = first.span.get_source_text(cx) + && let Some(local_snippet) = first.span.get_text(cx) { let variant = match (kind.node, lhs_kind.node, rhs_kind.node) { (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => Variant::ManualIsInfinite, diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs index 96d69003934fc..33370194d4e7c 100644 --- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs +++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use clippy_utils::visitors::{is_local_used, local_used_once}; use rustc_errors::Applicability; @@ -105,8 +105,8 @@ impl LateLintPass<'_> for ManualHashOne { finish_expr.span, "manual implementation of `BuildHasher::hash_one`", |diag| { - if let Some(build_hasher) = build_hasher.span.get_source_text(cx) - && let Some(hashed_value) = hashed_value.span.get_source_text(cx) + if let Some(build_hasher) = build_hasher.span.get_text(cx) + && let Some(hashed_value) = hashed_value.span.get_text(cx) { diag.multipart_suggestion( "try", diff --git a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs index f60b2a1a6729f..94a3f2b1f15f9 100644 --- a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::LitKind; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; @@ -143,8 +143,8 @@ impl LateLintPass<'_> for ManualRangePatterns { pat.span, "this OR pattern can be rewritten using a range", |diag| { - if let Some(min) = min.span.get_source_text(cx) - && let Some(max) = max.span.get_source_text(cx) + if let Some(min) = min.span.get_text(cx) + && let Some(max) = max.span.get_text(cx) { diag.span_suggestion( pat.span, diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs index 67685f803841c..2eb15928f25aa 100644 --- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::higher::{If, IfLetOrMatch}; use clippy_utils::msrvs::Msrv; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::{IntoSpan, SpanRangeExt, snippet}; +use clippy_utils::source::{IntoSpan, SpanExt, snippet}; use clippy_utils::usage::mutated_variables; use clippy_utils::visitors::is_local_used; use clippy_utils::{SpanlessEq, get_ref_operators, is_unit_expr, peel_blocks_with_stmt, peel_ref_operators}; diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs index e3a112d1f7802..e317056e29809 100644 --- a/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/matches/manual_unwrap_or.rs @@ -1,6 +1,6 @@ use clippy_utils::consts::ConstEvalCtxt; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; -use clippy_utils::source::{SpanRangeExt as _, indent_of, reindent_multiline}; +use clippy_utils::source::{SpanExt as _, indent_of, reindent_multiline}; use rustc_ast::{BindingMode, ByRef}; use rustc_errors::Applicability; use rustc_hir::def::Res; @@ -160,7 +160,7 @@ fn handle( ); } else if let Some(ty_name) = find_type_name(cx, cx.typeck_results().expr_ty(condition)) && cx.typeck_results().expr_adjustments(body_some).is_empty() - && let Some(or_body_snippet) = peel_blocks(body_none).span.get_source_text(cx) + && let Some(or_body_snippet) = peel_blocks(body_none).span.get_text(cx) && let Some(indent) = indent_of(cx, expr.span) && ConstEvalCtxt::new(cx).eval_local(body_none, expr.span.ctxt()).is_some() { diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs index caef6a0b36377..f500f21246bdb 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{SpanlessEq, fulfill_or_allowed, hash_expr, is_lint_allowed, search_same}; use core::cmp::Ordering; use core::{iter, slice}; @@ -152,7 +152,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) { if let Some(((_, dest), src)) = split && let Some(pat_snippets) = group .iter() - .map(|(_, arm)| arm.pat.span.get_source_text(cx)) + .map(|(_, arm)| arm.pat.span.get_text(cx)) .collect::>>() { let suggs = src diff --git a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs index 6b46791eb117a..98530b7808076 100644 --- a/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs +++ b/src/tools/clippy/clippy_lints/src/matches/match_wild_enum.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns}; use rustc_errors::Applicability; use rustc_hir::def::{CtorKind, DefKind, Res}; @@ -119,7 +119,7 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) { wildcard_ident.map_or(String::new(), |ident| { ident .span - .get_source_text(cx) + .get_text(cx) .map_or_else(|| format!("{} @ ", ident.name), |s| format!("{s} @ ")) }), if let CommonPrefixSearcher::Path(path_prefix) = path_prefix { diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs index ae0f3ffa0edc9..459c2bf1fef73 100644 --- a/src/tools/clippy/clippy_lints/src/matches/mod.rs +++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs @@ -26,14 +26,15 @@ mod wild_in_or_pats; use clippy_config::Conf; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::walk_span_to_context; +use clippy_utils::source::SpanExt; use clippy_utils::{ - higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg, span_extract_comments, sym, + higher, is_direct_expn_of, is_in_const_context, is_lint_allowed, is_span_match, sym, tokenize_with_text, }; use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind}; +use rustc_lexer::{TokenKind, is_whitespace}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::{SpanData, SyntaxContext}; +use rustc_span::Span; declare_clippy_lint! { /// ### What it does @@ -1084,34 +1085,38 @@ impl<'tcx> LateLintPass<'tcx> for Matches { try_err::check(cx, expr, ex); } - if !from_expansion && !contains_cfg_arm(cx, expr, ex, arms) { + if !from_expansion + && let mut has_cfg = false + && let mut has_comments = false + && walk_intra_arm_text(cx, expr.span, ex.span, arms, |s| { + let mut iter = tokenize_with_text(s).filter(|(t, ..)| match t { + TokenKind::Whitespace => false, + TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } => { + has_comments = true; + false + }, + _ => true, + }); + while let Some((t, ..)) = iter.next() { + if matches!(t, TokenKind::Pound) + && matches!(iter.next(), Some((TokenKind::OpenBracket, ..))) + && matches!(iter.next(), Some((TokenKind::Ident, "cfg", _))) + { + has_cfg = true; + } + } + }) + && !has_cfg + { if source == MatchSource::Normal { - if !(self.msrv.meets(cx, msrvs::MATCHES_MACRO) - && match_like_matches::check_match(cx, expr, ex, arms)) - { + let is_match_like_matches = self.msrv.meets(cx, msrvs::MATCHES_MACRO) + && match_like_matches::check_match(cx, expr, ex, arms); + if !(is_match_like_matches || is_lint_allowed(cx, MATCH_SAME_ARMS, expr.hir_id)) { match_same_arms::check(cx, arms); } redundant_pattern_match::check_match(cx, expr, ex, arms); - let mut match_comments = span_extract_comments(cx, expr.span); - // We remove comments from inside arms block. - if !match_comments.is_empty() { - for arm in arms { - for comment in span_extract_comments(cx, arm.body.span) { - if let Some(index) = match_comments - .iter() - .enumerate() - .find(|(_, cm)| **cm == comment) - .map(|(index, _)| index) - { - match_comments.remove(index); - } - } - } - } - // If there are still comments, it means they are outside of the arms. Tell the lint - // code about it. - single_match::check(cx, ex, arms, expr, !match_comments.is_empty()); + single_match::check(cx, ex, arms, expr, has_comments); match_bool::check(cx, ex, arms, expr); overlapping_arms::check(cx, ex, arms); match_wild_enum::check(cx, ex, arms); @@ -1216,64 +1221,51 @@ impl<'tcx> LateLintPass<'tcx> for Matches { } } -/// Checks if there are any arms with a `#[cfg(..)]` attribute. -fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>]) -> bool { - let Some(scrutinee_span) = walk_span_to_context(scrutinee.span, SyntaxContext::root()) else { - // Shouldn't happen, but treat this as though a `cfg` attribute were found - return true; - }; - - let start = scrutinee_span.hi(); - let mut arm_spans = arms.iter().map(|arm| { - let data = arm.span.data(); - (data.ctxt == SyntaxContext::root()).then_some((data.lo, data.hi)) - }); - let end = e.span.hi(); - - // Walk through all the non-code space before each match arm. The space trailing the final arm is - // handled after the `try_fold` e.g. - // - // match foo { - // _________^- everything between the scrutinee and arm1 - //| arm1 => (), - //|---^___________^ everything before arm2 - //| #[cfg(feature = "enabled")] - //| arm2 => some_code(), - //|---^____________________^ everything before arm3 - //| // some comment about arm3 - //| arm3 => some_code(), - //|---^____________________^ everything after arm3 - //| #[cfg(feature = "disabled")] - //| arm4 = some_code(), - //|}; - //|^ - let found = arm_spans.try_fold(start, |start, range| { - let Some((end, next_start)) = range else { - // Shouldn't happen as macros can't expand to match arms, but treat this as though a `cfg` attribute - // were found. - return Err(()); - }; - let span = SpanData { - lo: start, - hi: end, - ctxt: SyntaxContext::root(), - parent: None, - } - .span(); - (!span_contains_cfg(cx, span)).then_some(next_start).ok_or(()) - }); - match found { - Ok(start) => { - let span = SpanData { - lo: start, - hi: end, - ctxt: SyntaxContext::root(), - parent: None, +/// Calls the given function for each segment of the source text within the +/// match block which is not part of any arm. For the purposes of this function +/// attributes on an arm are not considered part of the arm. +/// +/// This will return whether all the relevant source text could be retrieved. If +/// all the source text cannot be retrieved it should be assumed that the match +/// originates from a macro. +#[must_use] +fn walk_intra_arm_text( + cx: &LateContext<'_>, + match_sp: Span, + scrutinee_sp: Span, + arms: &[Arm<'_>], + mut f: impl FnMut(&str), +) -> bool { + if let Some(src) = match_sp.get_source_range(cx) + && let scrutinee_sp = scrutinee_sp.source_callsite().data() + && let block_start = (scrutinee_sp.hi.0 - src.sf.start_pos.0) as usize + && let Some(src_text) = src.sf.src.as_ref().map(|x| &***x) + && let Some(block_text) = src_text.get(block_start..src.range.end) + && let Some(stripped_text) = block_text.trim_start_matches(is_whitespace).strip_prefix('{') + && let arms_start = block_start + (block_text.len() - stripped_text.len()) + && let Some(arms_end) = stripped_text + .trim_end_matches(|c| is_whitespace(c) || c == ')') + .strip_suffix('}') + .map(|s| src.range.end - (stripped_text.len() - s.len())) + && let Some(range) = arms.iter().try_fold(arms_start..arms_end, |range, arm| { + let arm_sp: rustc_span::SpanData = arm.span.source_callsite().data(); + let arm_range = (arm_sp.lo.0 - src.sf.start_pos.0) as usize..(arm_sp.hi.0 - src.sf.start_pos.0) as usize; + if range.start <= arm_range.start + && arm_range.end <= range.end + && let Some(src) = src_text.get(range.start..arm_range.start) + { + f(src); + Some(arm_range.end..range.end) + } else { + None } - .span(); - span_contains_cfg(cx, span) - }, - Err(()) => true, + }) + && let Some(src) = src_text.get(range) + { + f(src); + true + } else { + false } } diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs index b96c869b5151a..7c740ababb060 100644 --- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs +++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::{ - SpanRangeExt, expr_block, snippet, snippet_block_with_context, snippet_with_applicability, snippet_with_context, + SpanExt, expr_block, snippet, snippet_block_with_context, snippet_with_applicability, snippet_with_context, }; use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs}; use clippy_utils::{is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs, sym}; @@ -22,7 +22,7 @@ use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE}; /// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty /// match arms. fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool { - span.check_source_text(cx, |text| text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")) + span.check_text(cx, |text| text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")) } pub(crate) fn check<'tcx>( diff --git a/src/tools/clippy/clippy_lints/src/methods/by_ref_peekable_peek.rs b/src/tools/clippy/clippy_lints/src/methods/by_ref_peekable_peek.rs new file mode 100644 index 0000000000000..1faaf2c9c8e43 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/by_ref_peekable_peek.rs @@ -0,0 +1,84 @@ +use crate::clippy_utils::res::MaybeTypeckRes; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::{MaybeDef, MaybeResPath as _}; +use clippy_utils::sugg::Sugg; +use clippy_utils::sym; +use clippy_utils::ty::implements_trait; +use rustc_ast::BindingMode; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, LetStmt, Node, PatKind}; +use rustc_lint::LateContext; +use rustc_middle::ty; + +use super::BY_REF_PEEKABLE_PEEK; + +pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>) { + if let ExprKind::MethodCall(maybe_peekable, peekable_recv, [], _) = recv.kind + && maybe_peekable.ident.name == sym::peekable + && !peekable_recv.span.from_expansion() + && let ExprKind::MethodCall(maybe_by_ref, by_ref_recv, [], _) = peekable_recv.kind + && maybe_by_ref.ident.name == sym::by_ref + && !by_ref_recv.span.from_expansion() + && [peekable_recv, recv] + .into_iter() + .all(|e| cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Iterator)) + { + span_lint_and_then( + cx, + BY_REF_PEEKABLE_PEEK, + expr.span, + "calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output", + |diag| { + let span = by_ref_recv.span.shrink_to_hi().with_hi(expr.span.hi()); + if let ty::Ref(_, iter_ty, _) = cx.typeck_results().expr_ty_adjusted(by_ref_recv).kind() + && let Some(clone_trait) = cx.tcx.lang_items().clone_trait() + && implements_trait(cx, *iter_ty, clone_trait, &[]) + { + diag.span_suggestion_verbose( + span, + "to peek the first item without advancing the underlying iterator, use", + ".clone().next().as_ref()", + Applicability::MaybeIncorrect, + ); + } + diag.span_suggestion_verbose( + span, + "to advance the underlying iterator, use", + ".next().as_ref()", + Applicability::MaybeIncorrect, + ); + // If the iterator is a local variable, initialized through a simple binding with an inferred + // initialization expression, suggest making the initialization expression peekable. + if let Some(iter_local_id) = by_ref_recv.res_local_id() + && let Node::LetStmt(LetStmt { + pat: let_pat, + ty: None, + init: Some(init_expr), + els: None, + span: let_stmt_span, + .. + }) = cx.tcx.parent_hir_node(iter_local_id) + && let PatKind::Binding(BindingMode::MUT, _, _, None) = let_pat.kind + && !let_stmt_span.from_expansion() + // Changing the type of the iterator may prevent the code from compiling + && let mut app = Applicability::MaybeIncorrect + && let sugg = + Sugg::hir_with_context(cx, init_expr, let_stmt_span.ctxt(), "_", &mut app).maybe_paren() + { + diag.multipart_suggestion( + "to make the iterator peekable, use", + vec![ + (init_expr.span.source_callsite(), format!("{sugg}.peekable()")), + (recv.span.with_lo(by_ref_recv.span.hi()), String::new()), + ], + app, + ); + } else { + diag.help( + "you might want to transform the iterator itself using `.peekable()` without using `.by_ref()`", + ); + } + }, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs index 15f4d91e4bd9e..b6694ad5148ba 100644 --- a/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs +++ b/src/tools/clippy/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; +use clippy_utils::source::{SpanExt, indent_of, reindent_multiline}; use clippy_utils::sym; use rustc_ast::ast::LitKind; use rustc_errors::Applicability; @@ -51,7 +51,7 @@ pub(super) fn check<'tcx>( "case-sensitive file extension comparison", |diag| { diag.help("consider using a case-insensitive comparison instead"); - if let Some(recv_source) = recv.span.get_source_text(cx) { + if let Some(recv_source) = recv.span.get_text(cx) { let recv_source = if cx.typeck_results().expr_ty(recv).is_ref() { recv_source.to_owned() } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs b/src/tools/clippy/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs new file mode 100644 index 0000000000000..e151cf43a5044 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/chunks_exact_to_as_chunks.rs @@ -0,0 +1,104 @@ +use super::CHUNKS_EXACT_TO_AS_CHUNKS; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; +use clippy_utils::source::snippet_with_context; +use clippy_utils::visitors::is_const_evaluatable; +use clippy_utils::{get_expr_use_site, sym}; +use rustc_errors::Applicability; +use rustc_hir::{Expr, ExprKind, Node, PatKind}; +use rustc_lint::LateContext; +use rustc_middle::ty; +use rustc_span::{DesugaringKind, ExpnKind, Span, Symbol}; + +pub(super) fn check<'tcx>( + cx: &LateContext<'tcx>, + recv: &'tcx Expr<'tcx>, + arg: &'tcx Expr<'tcx>, + expr: &'tcx Expr<'tcx>, + call_span: Span, + method_name: Symbol, + msrv: Msrv, +) { + let recv_ty = cx.typeck_results().expr_ty_adjusted(recv); + if !matches!(recv_ty.kind(), ty::Ref(_, inner, _) if inner.is_slice()) { + return; + } + + if is_const_evaluatable(cx.tcx, cx.typeck_results(), arg) { + if !msrv.meets(cx, msrvs::AS_CHUNKS) { + return; + } + + let use_ctxt = get_expr_use_site(cx.tcx, cx.typeck_results(), expr.span.ctxt(), expr); + + if use_ctxt.is_ty_unified { + return; + } + + let suggestion_method = if method_name == sym::chunks_exact_mut { + "as_chunks_mut" + } else { + "as_chunks" + }; + + let mut applicability = Applicability::MachineApplicable; + let arg_str = snippet_with_context(cx, arg.span, expr.span.ctxt(), "_", &mut applicability).0; + + let as_chunks = format_args!("{suggestion_method}::<{arg_str}>()"); + + span_lint_and_then( + cx, + CHUNKS_EXACT_TO_AS_CHUNKS, + call_span, + format!("using `{method_name}` with a constant chunk size"), + |diag| { + if let Node::Expr(use_expr) = use_ctxt.node { + match use_expr.kind { + ExprKind::Call(_, [recv]) | ExprKind::MethodCall(_, recv, [], _) + if recv.hir_id == use_ctxt.child_id + && matches!( + use_expr.span.ctxt().outer_expn_data().kind, + ExpnKind::Desugaring(DesugaringKind::ForLoop), + ) => + { + diag.span_suggestion( + call_span, + "consider using `as_chunks` instead", + format!("{as_chunks}.0"), + applicability, + ); + return; + }, + ExprKind::MethodCall(_, recv, ..) + if recv.hir_id == use_ctxt.child_id + && cx + .ty_based_def(use_expr) + .assoc_fn_parent(cx) + .is_diag_item(cx, sym::Iterator) => + { + diag.span_suggestion( + call_span, + "consider using `as_chunks` instead", + format!("{as_chunks}.0.iter()"), + applicability, + ); + return; + }, + _ => {}, + } + } + + diag.span_help(call_span, format!("consider using `{as_chunks}` instead")); + + if let Node::LetStmt(let_stmt) = use_ctxt.node + && let PatKind::Binding(_, _, ident, _) = let_stmt.pat.kind + { + diag.note(format!( + "you can access the chunks using `{ident}.0.iter()`, and the remainder using `{ident}.1`" + )); + } + }, + ); + } +} diff --git a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs index cd6b39b15a1b9..0b6ead0747a80 100644 --- a/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/clear_with_drain.rs @@ -1,8 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; -use clippy_utils::{is_range_full, sym}; +use clippy_utils::res::{MaybeDef, MaybeResPath}; +use clippy_utils::{is_full_collection_range, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, QPath}; +use rustc_hir::{Expr, LangItem}; use rustc_lint::LateContext; use rustc_span::Span; @@ -16,8 +16,7 @@ const ACCEPTABLE_TYPES_WITHOUT_ARG: [rustc_span::Symbol; 3] = [sym::BinaryHeap, pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span: Span, arg: Option<&Expr<'_>>) { if let Some(arg) = arg { if match_acceptable_type(cx, recv, &ACCEPTABLE_TYPES_WITH_ARG) - && let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind - && is_range_full(cx, arg, Some(container_path)) + && is_full_collection_range(cx, recv.res_local_id(), arg) { suggest(cx, expr, recv, span); } diff --git a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs index 6c222640e2601..2b4b200904b2e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/drain_collect.rs @@ -1,67 +1,31 @@ use crate::methods::DRAIN_COLLECT; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::res::MaybeDef; +use clippy_utils::res::{MaybeDef, MaybeResPath}; use clippy_utils::source::snippet; -use clippy_utils::{is_range_full, std_or_core, sym}; +use clippy_utils::{is_full_collection_range, std_or_core, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, LangItem, Path, QPath}; +use rustc_hir::Expr; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_middle::ty::Ty; -use rustc_span::Symbol; -/// Checks if both types match the given diagnostic item, e.g.: -/// -/// `vec![1,2].drain(..).collect::>()` -/// ^^^^^^^^^ ^^^^^^ true -/// `vec![1,2].drain(..).collect::>()` -/// ^^^^^^^^^ ^^^^^^^^^^ false -fn types_match_diagnostic_item(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>, sym: Symbol) -> bool { - if let Some(expr_adt) = expr.ty_adt_def() - && let Some(recv_adt) = recv.ty_adt_def() - { - cx.tcx.is_diagnostic_item(sym, expr_adt.did()) && cx.tcx.is_diagnostic_item(sym, recv_adt.did()) - } else { - false - } -} - -/// Checks `std::{vec::Vec, collections::VecDeque}`. -fn check_vec(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool { - (types_match_diagnostic_item(cx, expr, recv, sym::Vec) - || types_match_diagnostic_item(cx, expr, recv, sym::VecDeque)) - && matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path))) -} - -/// Checks `std::string::String` -fn check_string(cx: &LateContext<'_>, args: &[Expr<'_>], expr: Ty<'_>, recv: Ty<'_>, recv_path: &Path<'_>) -> bool { - expr.is_lang_item(cx, LangItem::String) - && recv.is_lang_item(cx, LangItem::String) - && matches!(args, [arg] if is_range_full(cx, arg, Some(recv_path))) -} - -/// Checks `std::collections::{HashSet, HashMap, BinaryHeap}`. -fn check_collections(cx: &LateContext<'_>, expr: Ty<'_>, recv: Ty<'_>) -> Option<&'static str> { - types_match_diagnostic_item(cx, expr, recv, sym::HashSet) - .then_some("HashSet") - .or_else(|| types_match_diagnostic_item(cx, expr, recv, sym::HashMap).then_some("HashMap")) - .or_else(|| types_match_diagnostic_item(cx, expr, recv, sym::BinaryHeap).then_some("BinaryHeap")) -} - -pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, recv: &Expr<'_>) { - let expr_ty = cx.typeck_results().expr_ty(expr); - let recv_ty = cx.typeck_results().expr_ty(recv); - let recv_ty_no_refs = recv_ty.peel_refs(); - - if let ExprKind::Path(QPath::Resolved(_, recv_path)) = recv.kind - && let Some(typename) = check_vec(cx, args, expr_ty, recv_ty_no_refs, recv_path) - .then_some("Vec") - .or_else(|| check_string(cx, args, expr_ty, recv_ty_no_refs, recv_path).then_some("String")) - .or_else(|| check_collections(cx, expr_ty, recv_ty_no_refs)) +pub(super) fn check(cx: &LateContext<'_>, arg: Option<&Expr<'_>>, expr: &Expr<'_>, recv: &Expr<'_>) { + let ty = cx.typeck_results().expr_ty(recv); + let (is_ref, ty) = match *ty.kind() { + ty::Ref(_, ty, _) => (true, ty), + _ => (false, ty), + }; + if cx.typeck_results().expr_ty(expr) == ty + && let Some(did) = ty.opt_def_id() + && (cx.tcx.lang_items().string() == Some(did) + || matches!( + ty.opt_diag_name(cx), + Some(sym::HashMap | sym::HashSet | sym::BinaryHeap | sym::Vec | sym::VecDeque) + )) + && arg.is_none_or(|arg| is_full_collection_range(cx, recv.res_local_id(), arg)) && let Some(exec_context) = std_or_core(cx) { let recv = snippet(cx, recv.span, ""); - let sugg = if let ty::Ref(..) = recv_ty.kind() { + let sugg = if is_ref { format!("{exec_context}::mem::take({recv})") } else { format!("{exec_context}::mem::take(&mut {recv})") @@ -71,8 +35,8 @@ pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], expr: &Expr<'_>, re cx, DRAIN_COLLECT, expr.span, - format!("you seem to be trying to move all elements into a new `{typename}`"), - "consider using `mem::take`", + "draining all elements of a collection into a new collection of the same type", + "use `mem::take` to avoid creating a new allocation", sugg, Applicability::MachineApplicable, ); diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs index 89b37d507b702..97a03f9868b1d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs @@ -100,7 +100,7 @@ fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Ex } fn contains_call<'a>(cx: &LateContext<'a>, arg: &'a hir::Expr<'a>) -> bool { - for_each_expr(cx, arg, |expr| { + for_each_expr(cx.tcx, arg, |expr| { if matches!(expr.kind, hir::ExprKind::MethodCall { .. } | hir::ExprKind::Call { .. }) && !is_inside_always_const_context(cx.tcx, expr.hir_id) { diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs index 8e5b271212e67..419a7e0ed877d 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_bool_then.rs @@ -1,7 +1,7 @@ use super::FILTER_MAP_BOOL_THEN; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::ty::is_copy; use clippy_utils::{CaptureKind, can_move_expr_to_closure, contains_return, is_from_proc_macro, peel_blocks, sym}; use rustc_ast::Mutability; @@ -43,7 +43,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: & .iter() .filter(|adj| matches!(adj.kind, Adjust::Deref(_))) .count() - && let Some(param_snippet) = param.span.get_source_text(cx) + && let Some(param_snippet) = param.span.get_text(cx) { let mut applicability = Applicability::MachineApplicable; let (filter, _) = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut applicability); diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs index 9c6b16b88bfd1..3584833bc61cd 100644 --- a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs +++ b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs @@ -1,15 +1,15 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::source::snippet; +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::implements_trait; use clippy_utils::{path_to_local_with_projections, sym}; use rustc_ast::{BindingMode, Mutability}; use rustc_errors::Applicability; -use rustc_hir as hir; +use rustc_hir::{Expr, Node, PatKind}; use rustc_lint::LateContext; use super::FILTER_NEXT; -#[derive(Copy, Clone)] +#[derive(Clone, Copy)] pub(super) enum Direction { Forward, Backward, @@ -17,15 +17,13 @@ pub(super) enum Direction { /// lint use of `filter().next()` for `Iterator` and `filter().next_back()` for /// `DoubleEndedIterator` -pub(super) fn check<'tcx>( - cx: &LateContext<'tcx>, - expr: &'tcx hir::Expr<'_>, - recv: &'tcx hir::Expr<'_>, - filter_arg: &'tcx hir::Expr<'_>, +pub(super) fn check( + cx: &LateContext<'_>, + expr: &Expr<'_>, + recv: &Expr<'_>, + filter_arg: &Expr<'_>, direction: Direction, ) { - // lint if caller of `.filter().next()` is an Iterator or `.filter().next_back()` is a - // DoubleEndedIterator let (required_trait, next_method, find_method) = match direction { Direction::Forward => (sym::Iterator, "next", "find"), Direction::Backward => (sym::DoubleEndedIterator, "next_back", "rfind"), @@ -37,30 +35,31 @@ pub(super) fn check<'tcx>( { return; } - let msg = format!( - "called `filter(..).{next_method}()` on an `{}`. This is more succinctly expressed by calling \ - `.{find_method}(..)` instead", - required_trait.as_str() - ); - let filter_snippet = snippet(cx, filter_arg.span, ".."); - if filter_snippet.lines().count() <= 1 { - let iter_snippet = snippet(cx, recv.span, ".."); - // add note if not multi-line - span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| { - let (applicability, pat) = if let Some(id) = path_to_local_with_projections(recv) - && let hir::Node::Pat(pat) = cx.tcx.hir_node(id) - && let hir::PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind + span_lint_and_then( + cx, + FILTER_NEXT, + expr.span, + format!("called `filter(..).{next_method}()` on an `{required_trait}`"), + |diag| { + let mut app = Applicability::MachineApplicable; + let filter_snippet = snippet_with_applicability(cx, filter_arg.span, "..", &mut app); + let iter_snippet = snippet_with_applicability(cx, recv.span, "..", &mut app); + + let pat = if let Some(id) = path_to_local_with_projections(recv) + && let Node::Pat(pat) = cx.tcx.hir_node(id) + && let PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind { - (Applicability::Unspecified, Some((pat.span, ident))) + app = Applicability::Unspecified; + Some((pat.span, ident)) } else { - (Applicability::MachineApplicable, None) + None }; - diag.span_suggestion( + diag.span_suggestion_verbose( expr.span, - "try", + format!("use `.{find_method}(..)` instead"), format!("{iter_snippet}.{find_method}({filter_snippet})"), - applicability, + app, ); if let Some((pat_span, ident)) = pat { @@ -69,8 +68,6 @@ pub(super) fn check<'tcx>( format!("you will also need to make `{ident}` mutable, because `{find_method}` takes `&mut self`"), ); } - }); - } else { - span_lint(cx, FILTER_NEXT, expr.span, msg); - } + }, + ); } diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs index 8d1585baac451..2ef09fa3f8d2c 100644 --- a/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs +++ b/src/tools/clippy/clippy_lints/src/methods/iter_with_drain.rs @@ -1,7 +1,8 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::{is_range_full, sym}; +use clippy_utils::res::MaybeResPath; +use clippy_utils::{is_full_collection_range, sym}; use rustc_errors::Applicability; -use rustc_hir::{Expr, ExprKind, QPath}; +use rustc_hir::{Expr, ExprKind}; use rustc_lint::LateContext; use rustc_span::Span; @@ -12,8 +13,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, span && let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def() && let Some(ty_name) = cx.tcx.get_diagnostic_name(adt.did()) && matches!(ty_name, sym::Vec | sym::VecDeque) - && let ExprKind::Path(QPath::Resolved(None, container_path)) = recv.kind - && is_range_full(cx, arg, Some(container_path)) + && is_full_collection_range(cx, recv.res_local_id(), arg) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs index a89a656a6bc7e..5ef8450da93bc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use clippy_utils::ty::get_field_by_name; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures}; use clippy_utils::{ExprUseNode, get_expr_use_site, sym}; @@ -47,7 +47,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name: let can_lint = for_each_expr_without_closures(block.stmts, |e| { if let ExprKind::Closure(c) = e.kind { // Nested closures don't need to treat returns specially. - let _: Option = for_each_expr(cx, cx.tcx.hir_body(c.body).value, |e| { + let _: Option = for_each_expr(cx.tcx, cx.tcx.hir_body(c.body).value, |e| { if e.res_local_id() == Some(arg_id) { let (kind, same_ctxt) = check_use(cx, ctxt, e); match (kind, same_ctxt && e.span.ctxt() == ctxt) { diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs index 48f2c10f97cc6..050b28b3babd7 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_ok_or.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeQPath, MaybeResPath}; -use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; +use clippy_utils::source::{SpanExt, indent_of, reindent_multiline}; use rustc_errors::Applicability; use rustc_hir::LangItem::{ResultErr, ResultOk}; use rustc_hir::{Expr, ExprKind, PatKind}; @@ -27,8 +27,8 @@ pub(super) fn check<'tcx>( && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind && err_path.res(cx).ctor_parent(cx).is_lang_item(cx, ResultErr) && is_ok_wrapping(cx, map_expr) - && let Some(recv_snippet) = recv.span.get_source_text(cx) - && let Some(err_arg_snippet) = err_arg.span.get_source_text(cx) + && let Some(recv_snippet) = recv.span.get_text(cx) + && let Some(err_arg_snippet) = err_arg.span.get_text(cx) && let Some(indent) = indent_of(cx, expr.span) { let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.as_str(), true, Some(indent + 4)); diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_option_zip.rs b/src/tools/clippy/clippy_lints/src/methods/manual_option_zip.rs index aa23b9deff45b..538f8a2fe93dc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_option_zip.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_option_zip.rs @@ -1,4 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::eager_or_lazy::switch_to_eager_eval; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::peel_blocks; use clippy_utils::res::{MaybeDef, MaybeResPath}; @@ -29,6 +30,8 @@ pub(super) fn check<'tcx>( && let ExprKind::MethodCall(method_path, map_recv, [map_arg], _) = peel_blocks(outer_value).kind && method_path.ident.name == sym::map && cx.typeck_results().expr_ty(map_recv).is_diag_item(cx, sym::Option) + // `b` is not lazy evaluated + && switch_to_eager_eval(cx, map_recv) // `b` does not reference the outer closure parameter `a`. && !local_used_in(cx, outer_param_id, map_recv) // `|b| (a, b)` diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs index 5f5944d5d4230..6ef7abffb0b2f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs +++ b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::implements_trait; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; @@ -31,14 +31,12 @@ pub(super) fn check<'tcx>( && let ExprKind::Closure(closure) = acc.kind && msrv.meets(cx, msrvs::ITERATOR_TRY_FOLD) && !is_from_proc_macro(cx, expr) - && let Some(args_snip) = closure - .fn_arg_span - .and_then(|fn_arg_span| fn_arg_span.get_source_text(cx)) + && let Some(args_snip) = closure.fn_arg_span.and_then(|fn_arg_span| fn_arg_span.get_text(cx)) { let init_snip = rest .is_empty() .then_some(first.span) - .and_then(|span| span.get_source_text(cx)) + .and_then(|span| span.get_text(cx)) .map_or_else(|| "...".to_owned(), |src| src.to_owned()); span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs index ad950f75f8130..14a9752d3bded 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_expr_identity_function; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; @@ -24,7 +24,7 @@ pub(super) fn check( && cx.ty_based_def(recv).opt_parent(cx).is_diag_item(cx, sym::Iterator) && is_expr_identity_function(cx, any_arg) && let map_any_call_span = map_call_span.with_hi(any_call_span.hi()) - && let Some(map_arg) = map_arg.span.get_source_text(cx) + && let Some(map_arg) = map_arg.span.get_text(cx) { span_lint_and_then( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs index ac2f991804865..ec6dccd4fe543 100644 --- a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs +++ b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs @@ -128,10 +128,10 @@ pub(super) fn check<'tcx>( (SuggestedKind::AndThen, _) => "and_then", (SuggestedKind::IsVariantAnd, sym::Result) => "is_ok_and", (SuggestedKind::IsVariantAnd, sym::Option) => "is_some_and", - (SuggestedKind::Other, _) - if unwrap_arg_ty.peel_refs().is_array() - && cx.typeck_results().expr_ty_adjusted(unwrap_arg).peel_refs().is_slice() => - { + (SuggestedKind::Other, _) if unwrap_arg_ty != cx.typeck_results().expr_ty_adjusted(unwrap_arg) => { + // If the `unwrap_or` argument needs an adjustment, moving it into `map_or`'s + // first argument can make type inference pick the unadjusted type and reject + // the closure return type. Keep the lint, but don't emit a rustfix. return; }, _ => "map_or", diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs index b01cd49739431..38a3b1705ad17 100644 --- a/src/tools/clippy/clippy_lints/src/methods/mod.rs +++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs @@ -1,4 +1,5 @@ mod bind_instead_of_map; +mod by_ref_peekable_peek; mod bytecount; mod bytes_count_to_len; mod bytes_nth; @@ -9,6 +10,7 @@ mod chars_last_cmp; mod chars_last_cmp_with_unwrap; mod chars_next_cmp; mod chars_next_cmp_with_unwrap; +mod chunks_exact_to_as_chunks; mod clear_with_drain; mod clone_on_copy; mod clone_on_ref_ptr; @@ -141,6 +143,7 @@ mod unnecessary_map_or_else; mod unnecessary_min_or_max; mod unnecessary_sort_by; mod unnecessary_to_owned; +mod unnecessary_unwrap_unchecked; mod unwrap_expect_used; mod useless_asref; mod useless_nonzero_new_unchecked; @@ -201,6 +204,39 @@ declare_clippy_lint! { "using `Option.and_then(|x| Some(y))`, which is more succinctly expressed as `map(|x| y)`" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usages of `Iterator::by_ref().peekable().peek()`. + /// + /// ### Why is this bad? + /// While it might look like this will allow peeking on the first + /// element of an iterator without consuming it and without consuming + /// the iterator itself, it will in practice consume the first element. + /// + /// The implementation of `Peekable::peek()` produces the first element + /// of the underlying iterator, and stores it internally so that it can + /// be later produced. As a consequence, it advances the underlying + /// iterator, whose `.next()` method will now produce its second element. + /// + /// ### Example + /// ```no_run + /// let mut iter = [1, 2, 3].into_iter(); + /// let x = iter.by_ref().peekable().peek(); // 1 + /// let y = iter.by_ref().peekable().peek(); // 2 + /// ``` + /// If this does what you intended, use the following instead, which is + /// shorter and clearer: + /// ```no_run + /// let mut iter = [1, 2, 3].into_iter(); + /// let x = iter.next().as_ref(); // 1 + /// let y = iter.next().as_ref(); // 2 + /// ``` + #[clippy::version = "1.98.0"] + pub BY_REF_PEEKABLE_PEEK, + suspicious, + "Using `.by_ref().peekable().peek()` on an iterator" +} + declare_clippy_lint! { /// ### What it does /// It checks for `str::bytes().count()` and suggests replacing it with @@ -329,6 +365,32 @@ declare_clippy_lint! { "using `.chars().next()` to check if a string starts with a char" } +declare_clippy_lint! { + /// ### What it does + /// Checks for usage of `chunks_exact` or `chunks_exact_mut` with a constant chunk size. + /// + /// ### Why is this bad? + /// `as_chunks` provides better ergonomics and type safety by returning arrays instead of slices. + /// It was stabilized in Rust 1.88. + /// + /// ### Example + /// ```no_run + /// let slice = [1, 2, 3, 4, 5, 6]; + /// let mut it = slice.chunks_exact(2); + /// for chunk in it {} + /// ``` + /// Use instead: + /// ```no_run + /// let slice = [1, 2, 3, 4, 5, 6]; + /// let (chunks, remainder) = slice.as_chunks::<2>(); + /// for chunk in chunks {} + /// ``` + #[clippy::version = "1.93.0"] + pub CHUNKS_EXACT_TO_AS_CHUNKS, + style, + "using `chunks_exact` with constant when `as_chunks` is more ergonomic" +} + declare_clippy_lint! { /// ### What it does /// Checks for usage of `.drain(..)` for the sole purpose of clearing a container. @@ -4541,6 +4603,34 @@ declare_clippy_lint! { "unnecessary calls to `to_owned`-like functions" } +declare_clippy_lint! { + /// ### What it does + /// Checks for calls to `unwrap_unchecked` when an `_unchecked` variant of the function exists. + /// + /// ### Why is this bad? + /// Calling the non-unchecked variant may result in checking that is then discarded + /// if `unwrap_unchecked` is called directly afterwards, whereas the unchecked + /// variant most likely avoids performing the check completely. + /// + /// ### Known problems + /// + /// The unchecked variant is only suggested if it's defined in the same `impl` block + /// as the non-unchecked one + /// + /// ### Example + /// ```rust + /// let s = unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + /// ``` + /// Use instead: + /// ```rust + /// let s = unsafe { std::str::from_utf8_unchecked(&[]) }; + /// ``` + #[clippy::version = "1.98.0"] + pub UNNECESSARY_UNWRAP_UNCHECKED, + complexity, + "calling `unwrap_unchecked` on a function which has an `_unchecked` variant" +} + declare_clippy_lint! { /// ### What it does /// Checks for usages of the following functions with an argument that constructs a default value @@ -4835,9 +4925,11 @@ impl_lint_pass!(Methods => [ BIND_INSTEAD_OF_MAP, BYTES_COUNT_TO_LEN, BYTES_NTH, + BY_REF_PEEKABLE_PEEK, CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS, CHARS_LAST_CMP, CHARS_NEXT_CMP, + CHUNKS_EXACT_TO_AS_CHUNKS, CLEAR_WITH_DRAIN, CLONED_INSTEAD_OF_COPIED, CLONE_ON_COPY, @@ -4979,6 +5071,7 @@ impl_lint_pass!(Methods => [ UNNECESSARY_RESULT_MAP_OR_ELSE, UNNECESSARY_SORT_BY, UNNECESSARY_TO_OWNED, + UNNECESSARY_UNWRAP_UNCHECKED, UNWRAP_OR_DEFAULT, UNWRAP_USED, USELESS_ASREF, @@ -5198,12 +5291,15 @@ impl Methods { _ => {}, } }, + (name @ (sym::chunks_exact | sym::chunks_exact_mut), [arg]) => { + chunks_exact_to_as_chunks::check(cx, recv, arg, expr, call_span, name, self.msrv); + }, (sym::and_then, [arg]) => { manual_option_zip::check(cx, expr, recv, arg, self.msrv); let biom_option_linted = bind_instead_of_map::check_and_then_some(cx, expr, recv, arg); let biom_result_linted = bind_instead_of_map::check_and_then_ok(cx, expr, recv, arg); if !biom_option_linted && !biom_result_linted { - let ule_and_linted = unnecessary_lazy_eval::check(cx, expr, recv, arg, "and"); + let ule_and_linted = unnecessary_lazy_eval::check(cx, expr, recv, arg, "and", true); if !ule_and_linted { return_and_then::check(cx, expr, recv, arg); } @@ -5277,8 +5373,10 @@ impl Methods { manual_str_repeat::check(cx, expr, recv, take_self_arg, take_arg); } }, - Some((sym::drain, recv, args, ..)) => { - drain_collect::check(cx, args, expr, recv); + Some((sym::drain, recv, args, ..)) => match args { + [arg] => drain_collect::check(cx, Some(arg), expr, recv), + [] => drain_collect::check(cx, None, expr, recv), + _ => {}, }, _ => {}, } @@ -5333,7 +5431,11 @@ impl Methods { } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, - (sym::expect_err, [_]) | (sym::unwrap_err | sym::unwrap_unchecked | sym::unwrap_err_unchecked, []) => { + (sym::unwrap_unchecked, []) => { + unnecessary_unwrap_unchecked::check(cx, expr, recv, call_span); + unnecessary_literal_unwrap::check(cx, expr, recv, name, args); + }, + (sym::expect_err, [_]) | (sym::unwrap_err | sym::unwrap_err_unchecked, []) => { unnecessary_literal_unwrap::check(cx, expr, recv, name, args); }, (sym::extend, [arg]) => { @@ -5439,7 +5541,7 @@ impl Methods { get_last_with_len::check(cx, expr, recv, arg); }, (sym::get_or_insert_with, [arg]) => { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert"); + unnecessary_lazy_eval::check(cx, expr, recv, arg, "get_or_insert", false); }, (sym::hash, [arg]) => { unit_hash::check(cx, expr, recv, arg); @@ -5594,16 +5696,19 @@ impl Methods { ptr_offset_by_literal::check(cx, expr, self.msrv); }, (sym::ok_or_else, [arg]) => { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or"); + unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or", true); }, (sym::open, [_]) => { open_options::check(cx, expr, recv); }, (sym::or_else, [arg]) => { if !bind_instead_of_map::check_or_else_err(cx, expr, recv, arg) { - unnecessary_lazy_eval::check(cx, expr, recv, arg, "or"); + unnecessary_lazy_eval::check(cx, expr, recv, arg, "or", false); } }, + (sym::peek, []) => { + by_ref_peekable_peek::check(cx, expr, recv); + }, (sym::push, [arg]) => { path_buf_push_overwrite::check(cx, expr, arg); }, @@ -5699,7 +5804,7 @@ impl Methods { if !self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) { return; } - unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some"); + unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some", true); }, (sym::try_into, []) if cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::TryInto) => { unnecessary_fallible_conversions::check_method(cx, expr); @@ -5789,7 +5894,7 @@ impl Methods { ); }, _ => { - unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or"); + unnecessary_lazy_eval::check(cx, expr, recv, u_arg, "unwrap_or", false); }, } unnecessary_literal_unwrap::check(cx, expr, recv, name, args); diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs index 948ed8a25746e..20468784aae7a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_character_iteration.rs @@ -8,7 +8,7 @@ use rustc_span::Span; use super::NEEDLESS_CHARACTER_ITERATION; use super::utils::get_last_chain_binding_hir_id; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{peel_blocks, sym}; fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> { @@ -36,7 +36,7 @@ fn handle_expr( && receiver.res_local_id() == Some(first_param) && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs() && *char_arg_ty.kind() == ty::Char - && let Some(snippet) = before_chars.get_source_text(cx) + && let Some(snippet) = before_chars.get_text(cx) { span_lint_and_sugg( cx, @@ -78,7 +78,7 @@ fn handle_expr( if revert != is_all && fn_path.ty_rel_def(cx).is_diag_item(cx, sym::char_is_ascii) && peels_expr_ref(arg).res_local_id() == Some(first_param) - && let Some(snippet) = before_chars.get_source_text(cx) + && let Some(snippet) = before_chars.get_text(cx) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs index 06e6a3c70b87d..7626cdb5a5fe8 100644 --- a/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/needless_option_as_deref.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use clippy_utils::usage::local_used_after_expr; use rustc_errors::Applicability; @@ -32,7 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name expr.span, "derefed type is same as origin", "try", - recv.span.get_source_text(cx).unwrap().to_owned(), + recv.span.get_text(cx).unwrap().to_owned(), Applicability::MachineApplicable, ); } diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs index a07cd5a8925ae..6f9e817490c12 100644 --- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs +++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs @@ -30,9 +30,26 @@ pub(super) fn check<'tcx>( args: &'tcx [hir::Expr<'_>], msrv: Msrv, ) { + // Bail out early unless the method is one that `check_unwrap_or_default` or + // `check_or_fn_call` can lint, to avoid walking the argument of every method call. + if !matches!( + name, + sym::unwrap_or + | sym::unwrap_or_else + | sym::or_insert + | sym::or_insert_with + | sym::get_or_insert + | sym::map_or + | sym::ok_or + | sym::or + | sym::and + ) { + return; + } + if let [arg] = args { let inner_arg = peel_blocks(arg); - for_each_expr(cx, inner_arg, |ex| { + for_each_expr(cx.tcx, inner_arg, |ex| { // `or_fun_call` lint needs to take nested expr into account, // but `unwrap_or_default` lint doesn't, we don't want something like: // `opt.unwrap_or(Foo { inner: String::default(), other: 1 })` to get replaced by @@ -72,7 +89,7 @@ pub(super) fn check<'tcx>( // `map_or` takes two arguments if let [arg, lambda] = args { let inner_arg = peel_blocks(arg); - for_each_expr(cx, inner_arg, |ex| { + for_each_expr(cx.tcx, inner_arg, |ex| { let is_top_most_expr = ex.hir_id == inner_arg.hir_id; match ex.kind { hir::ExprKind::Call(fun, fun_args) => { @@ -112,6 +129,12 @@ fn check_unwrap_or_default( method_span: Span, msrv: Msrv, ) -> bool { + let sugg = match (name, call_expr.is_some()) { + (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, + (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, + _ => return false, + }; + let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs(); // Check MSRV, but only for `Result::unwrap_or_default` @@ -150,12 +173,6 @@ fn check_unwrap_or_default( } }; - let sugg = match (name, call_expr.is_some()) { - (sym::unwrap_or, true) | (sym::unwrap_or_else, false) => sym::unwrap_or_default, - (sym::or_insert, true) | (sym::or_insert_with, false) => sym::or_default, - _ => return false, - }; - let Some(suggested_method_def_id) = receiver_ty.ty_adt_def().and_then(|adt_def| { cx.tcx .inherent_impls(adt_def.did()) diff --git a/src/tools/clippy/clippy_lints/src/methods/ptr_offset_by_literal.rs b/src/tools/clippy/clippy_lints/src/methods/ptr_offset_by_literal.rs index b5d2add65cf19..17f81f547aeb0 100644 --- a/src/tools/clippy/clippy_lints/src/methods/ptr_offset_by_literal.rs +++ b/src/tools/clippy/clippy_lints/src/methods/ptr_offset_by_literal.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -92,7 +92,7 @@ fn expr_as_literal<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opti } fn format_isize_literal<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { - let text = expr.span.get_source_text(cx)?; + let text = expr.span.get_text(cx)?; let text = peel_parens_str(&text); Some(text.trim_end_matches("isize").trim_end_matches('_').to_string()) } diff --git a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs index 5fec0c5f2cf7d..b01adbf4d58af 100644 --- a/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs +++ b/src/tools/clippy/clippy_lints/src/methods/range_zip_with_len.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeTypeckRes}; -use clippy_utils::source::{SpanRangeExt as _, snippet_with_applicability}; +use clippy_utils::source::{SpanExt as _, snippet_with_applicability}; use clippy_utils::{SpanlessEq, get_parent_expr, higher, is_integer_literal, sym}; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind, Node, Pat, PatKind, QPath}; @@ -49,8 +49,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, recv: &' ), )]; if let Some((left, right)) = invert_bindings - && let Some(snip_left) = left.get_source_text(cx) - && let Some(snip_right) = right.get_source_text(cx) + && let Some(snip_left) = left.get_text(cx) + && let Some(snip_right) = right.get_text(cx) { suggestions.extend([(left, snip_right.to_string()), (right, snip_left.to_string())]); } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/str_split.rs b/src/tools/clippy/clippy_lints/src/methods/str_split.rs index 8641f7c0abec3..fbb3dd41c7a4e 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_split.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_split.rs @@ -22,8 +22,9 @@ pub(super) fn check<'a>( // basic ones: a `'\n'`, `"\n"`, and `"\r\n"`. if let ExprKind::MethodCall(trim_method_name, trim_recv, [], trim_span) = split_recv.kind && trim_method_name.ident.name == sym::trim - && cx.typeck_results().expr_ty_adjusted(trim_recv).peel_refs().is_str() - && !is_const_evaluatable(cx, trim_recv) + && let typeck = cx.typeck_results() + && typeck.expr_ty_adjusted(trim_recv).peel_refs().is_str() + && !is_const_evaluatable(cx.tcx, typeck, trim_recv) && let ExprKind::Lit(split_lit) = split_arg.kind && matches!( split_lit.node, diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs index fff203296bce9..13782a492cbc5 100644 --- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs +++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs @@ -214,7 +214,7 @@ fn indirect_usage<'tcx>( }) = stmt.kind { let mut path_to_binding = None; - let _: Option = for_each_expr(cx, init_expr, |e| { + let _: Option = for_each_expr(cx.tcx, init_expr, |e| { if e.res_local_id() == Some(binding) { path_to_binding = Some(e); } diff --git a/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs b/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs index 48e89c2998efa..53c695e378476 100644 --- a/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs +++ b/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::is_from_proc_macro; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::{MaybeDef, MaybeResPath, MaybeTypeckRes}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use itertools::Itertools; use rustc_ast::LitKind; use rustc_errors::Applicability; @@ -35,7 +35,7 @@ pub(super) fn check<'tcx>( } && msrv.meets(cx, msrvs::MATCHES_MACRO) && !is_from_proc_macro(cx, expr) - && let Some(scrutinee_snip) = scrutinee.span.get_source_text(cx) + && let Some(scrutinee_snip) = scrutinee.span.get_text(cx) { // Normalize the char using `map` so `join` doesn't use `Display`, if we don't then // something like `r"\"` will become `'\'`, which is of course invalid diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs index d322909bef359..43dac249f7440 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_first_then_check.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -29,8 +29,8 @@ pub(super) fn check( }; let both_calls_span = first_call_span.with_hi(call_span.hi()); - if let Some(both_calls_snippet) = both_calls_span.get_source_text(cx) - && let Some(first_caller_snippet) = first_caller.span.get_source_text(cx) + if let Some(both_calls_snippet) = both_calls_span.get_text(cx) + && let Some(first_caller_snippet) = first_caller.span.get_text(cx) { let (sugg_span, suggestion) = if is_some { ( diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs index b2413bb77aa73..b622a219f35dc 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_get_then_check.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use rustc_errors::Applicability; @@ -39,11 +39,11 @@ pub(super) fn check( return; }; let both_calls_span = get_call_span.with_hi(call_span.hi()); - if let Some(snippet) = both_calls_span.get_source_text(cx) - && let Some(arg_snippet) = arg.span.get_source_text(cx) + if let Some(snippet) = both_calls_span.get_text(cx) + && let Some(arg_snippet) = arg.span.get_text(cx) { let generics_snippet = if let Some(generics) = path.args - && let Some(generics_snippet) = generics.span_ext.get_source_text(cx) + && let Some(generics_snippet) = generics.span_ext.get_text(cx) { format!("::{generics_snippet}") } else { @@ -64,7 +64,7 @@ pub(super) fn check( suggestion, Applicability::MaybeIncorrect, ); - } else if let Some(caller_snippet) = get_caller.span.get_source_text(cx) { + } else if let Some(caller_snippet) = get_caller.span.get_text(cx) { let full_span = get_caller.span.with_hi(call_span.hi()); span_lint_and_then( diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs index 444d0a1d72422..5b19c43f1b32f 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs @@ -2,7 +2,7 @@ use super::utils::clone_or_copy_needed; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::ForLoop; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::{get_iterator_item_ty, implements_trait}; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr}; @@ -42,7 +42,7 @@ pub fn check_for_loop_iter( && let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent) && let (clone_or_copy_needed, references_to_binding) = clone_or_copy_needed(cx, pat, body) && !clone_or_copy_needed - && let Some(receiver_snippet) = receiver.span.get_source_text(cx) + && let Some(receiver_snippet) = receiver.span.get_text(cx) { // Issue 12098 // https://github.com/rust-lang/rust-clippy/issues/12098 @@ -102,7 +102,7 @@ pub fn check_for_loop_iter( && implements_trait(cx, collection_ty, into_iterator_trait_id, &[]) && let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, sym::Item) && iter_item_ty == into_iter_item_ty - && let Some(collection_snippet) = collection.span.get_source_text(cx) + && let Some(collection_snippet) = collection.span.get_text(cx) { collection_snippet } else { diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs index 2869547650f31..0b3c8146b9f76 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; -use clippy_utils::source::snippet; +use clippy_utils::source::{snippet, snippet_with_applicability}; use clippy_utils::{eager_or_lazy, is_from_proc_macro, usage}; use hir::FnRetTy; use rustc_errors::Applicability; @@ -18,6 +18,7 @@ pub(super) fn check<'tcx>( recv: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>, simplify_using: &str, + use_turbofish: bool, ) -> bool { let is_option = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Option); let is_result = cx.typeck_results().expr_ty(recv).is_diag_item(cx, sym::Result); @@ -46,23 +47,43 @@ pub(super) fn check<'tcx>( } else { "unnecessary closure used with `bool::then`" }; - let applicability = if body + + let mut applicability = Applicability::MachineApplicable; + if body .params .iter() // bindings are checked to be unused above - .all(|param| matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) - && matches!( - fn_decl.output, - FnRetTy::DefaultReturn(_) - | FnRetTy::Return(hir::Ty { - kind: hir::TyKind::Infer(()), - .. - }) - ) { - Applicability::MachineApplicable - } else { - // replacing the lambda may break type inference - Applicability::MaybeIncorrect + .any(|param| !matches!(param.pat.kind, hir::PatKind::Binding(..) | hir::PatKind::Wild)) + { + // If the closure parameters have a pattern, + // it might be required for type inferrence. + applicability = Applicability::MaybeIncorrect; + } + let (ascription, turbofish) = match fn_decl.output { + FnRetTy::DefaultReturn(_) + | FnRetTy::Return(hir::Ty { + kind: hir::TyKind::Infer(()), + .. + }) => { + // if the closure has no explicit return type, + // then there's nothing to preserve + (String::new(), String::new()) + }, + FnRetTy::Return(ty) => { + // explicit return type was given on the closure + // + // we can preserve this information using `as`, but `as` is + // a somewhat dangerous feature, because it can be used to + // truncate integers + // + // if possible, use turbofish to preserve the type information + let ty = snippet_with_applicability(cx, ty.span, "_", &mut applicability); + if use_turbofish { + (String::new(), format!("::<{ty}>")) + } else { + (format!(" as {ty}"), String::new()) + } + }, }; // This is a duplicate of what's happening in clippy_lints::methods::method_call, @@ -73,7 +94,10 @@ pub(super) fn check<'tcx>( diag.span_suggestion_verbose( span, format!("use `{simplify_using}` instead"), - format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")), + format!( + "{simplify_using}{turbofish}({}{ascription})", + snippet(cx, body_expr.span, "..") + ), applicability, ); }); diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs index 391209d8c3657..60bc4de69237a 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs @@ -262,7 +262,15 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>) -> Option< if mirrored_exprs(left_expr, right_expr, &binding_map, BindingSource::Left) { (left_expr, l_pat.span, false) } else if mirrored_exprs(left_expr, right_expr, &binding_map, BindingSource::Right) { - (left_expr, r_pat.span, true) + // Use the right-hand expr (the `a` side) as the key body, peeling any `&` + // introduced by the `.cmp(&rhs)` call so the suggestion doesn't contain a + // spurious borrow. + let right_body = if let ExprKind::AddrOf(_, _, inner) = right_expr.kind { + inner + } else { + right_expr + }; + (right_body, l_pat.span, true) } else { return None; }; diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index a56dcd894b6aa..35ae44591c188 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -3,7 +3,7 @@ use super::unnecessary_iter_cloned::{self, is_into_iter}; use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet, snippet_with_context}; use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, peel_and_count_ty_refs}; use clippy_utils::visitors::find_all_ret_expressions; use clippy_utils::{fn_def_id, get_parent_expr, is_expr_temporary_value, return_ty, sym}; @@ -217,7 +217,7 @@ fn check_into_iter_call_arg( && let parent_ty = cx.typeck_results().expr_ty(parent) && implements_trait(cx, parent_ty, iterator_trait_id, &[]) && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty) - && let Some(receiver_snippet) = receiver.span.get_source_text(cx) + && let Some(receiver_snippet) = receiver.span.get_text(cx) // If the receiver is a `Cow`, we can't remove the `into_owned` generally, see https://github.com/rust-lang/rust-clippy/issues/13624. && !cx.typeck_results().expr_ty(receiver).is_diag_item(cx, sym::Cow) // Calling `iter()` on a temporary object can lead to false positives. #14242 @@ -313,8 +313,8 @@ fn check_string_from_utf8<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool { if let Some(parent) = get_parent_expr(cx, expr) && let Some((sym::split, argument_expr)) = get_fn_name_and_arg(cx, parent) - && let Some(receiver_snippet) = receiver.span.get_source_text(cx) - && let Some(arg_snippet) = argument_expr.span.get_source_text(cx) + && let Some(receiver_snippet) = receiver.span.get_text(cx) + && let Some(arg_snippet) = argument_expr.span.get_text(cx) { // We may end-up here because of an expression like `x.to_string().split(…)` where the type of `x` // implements `AsRef` but does not implement `Deref`. In this case, we have to @@ -712,7 +712,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx && let arg_ty = arg_ty.peel_refs() // For now we limit this lint to `String` and `Vec`. && (is_str_and_string(cx, arg_ty, original_arg_ty) || is_slice_and_vec(cx, arg_ty, original_arg_ty)) - && let Some(snippet) = caller.span.get_source_text(cx) + && let Some(snippet) = caller.span.get_text(cx) { span_lint_and_sugg( cx, diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_unwrap_unchecked.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_unwrap_unchecked.rs new file mode 100644 index 0000000000000..3f7f146446361 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_unwrap_unchecked.rs @@ -0,0 +1,276 @@ +use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::res::MaybeQPath; +use clippy_utils::ty::{option_or_result_arg_ty, same_type_modulo_regions}; +use clippy_utils::{is_from_proc_macro, last_path_segment, over}; +use rustc_errors::Applicability; +use rustc_hir::def::{DefKind, Namespace, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::{Body, Expr, ExprKind, PatKind, Safety}; +use rustc_lint::LateContext; +use rustc_middle::ty::{self, Ty}; +use rustc_span::Span; +use rustc_span::symbol::Ident; + +use super::UNNECESSARY_UNWRAP_UNCHECKED; + +#[derive(Clone, Copy, Debug)] +enum Variant { + /// Free `fn` in a module + Fn, + /// Associated item from an `impl` + Assoc(AssocKind), +} + +impl Variant { + fn msg(self) -> &'static str { + // Don't use `format!` instead -- it won't be optimized out. + match self { + Variant::Fn => "usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists", + Variant::Assoc(AssocKind::Fn) => { + "usage of `unwrap_unchecked` when an `_unchecked` variant of the associated function exists" + }, + Variant::Assoc(AssocKind::Method) => { + "usage of `unwrap_unchecked` when an `_unchecked` variant of the method exists" + }, + } + } +} + +/// This only exists so the help message shows `associated function` or `method`, depending on +/// whether it has a `self` parameter. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum AssocKind { + /// No `self`: `fn new() -> Self` + Fn, + /// Has `self`: `fn ty<'tcx>(&self) -> Ty<'tcx>` + Method, +} + +impl AssocKind { + fn new(fn_has_self_parameter: bool) -> Self { + if fn_has_self_parameter { Self::Method } else { Self::Fn } + } +} + +fn unchecked_ident(checked_ident: Ident) -> Option { + let checked_ident = checked_ident.to_string(); + // Only add `_unchecked` if it doesn't already end with `_` + (!checked_ident.ends_with('_')).then(|| Ident::from_str(&(checked_ident + "_unchecked"))) +} + +/// Find a function called the same as `checked`, but with added `_unchecked`. +/// +/// This doesn't check if the methods are actually "similar" -- for that, see +/// [`same_functions_modulo_safety`] +fn find_unchecked_sibling_fn( + cx: &LateContext<'_>, + checked_def_id: DefId, + checked_ident: Ident, +) -> Option<(DefId, Ident)> { + // Don't use `parent_module`. We only want to lint if its first parent is a `Mod`, + // i.e. if this is a free-standing function + let parent = cx.tcx.parent(checked_def_id); + if cx.tcx.def_kind(parent) == DefKind::Mod + && let children = parent.as_local().map_or_else( + || cx.tcx.module_children(parent), + // We must use a !query for local modules to prevent an ICE. + |parent| cx.tcx.module_children_local(parent), + ) + // Make sure that there are other functions in this module + // (otherwise there couldn't be an unchecked version) + && children.len() > 1 + && let Some(unchecked_ident) = unchecked_ident(checked_ident) + && let Some(unchecked_def_id) = children.iter().find_map(|child| { + if child.ident == unchecked_ident + && let Res::Def(DefKind::Fn, def_id) = child.res + { + Some(def_id) + } else { + None + } + }) + { + Some((unchecked_def_id, unchecked_ident)) + } else { + None + } +} + +/// Find a method called the same as `checked`, but with added `_unchecked`. +/// +/// This doesn't check if the methods are actually "similar" -- for that, see +/// [`same_functions_modulo_safety`] +fn find_unchecked_sibling_method<'tcx>( + cx: &LateContext<'tcx>, + checked_def_id: DefId, + checked_ident: Ident, +) -> Option<(&'tcx ty::AssocItem, Ident)> { + // Don't use `parent_impl`. We only want to lint if its first parent is an `Impl` + let parent = cx.tcx.parent(checked_def_id); + if matches!(cx.tcx.def_kind(parent), DefKind::Impl { .. }) + && let Some(unchecked_ident) = unchecked_ident(checked_ident) + // Only look in the same impl (to avoid dealing with generics etc.) + && let Some(unchecked) = cx.tcx.associated_items(parent).find_by_ident_and_namespace( + cx.tcx, + unchecked_ident, + Namespace::ValueNS, + parent, + ) + { + Some((unchecked, unchecked_ident)) + } else { + None + } +} + +/// Checks that `checked_def_id` and `unchecked_def_id` refer to functions with: +/// - same visibility +/// - identical signatures, apart from unsafety +/// - "matching" return types: the checked version returns `Option`/`Result`, while the +/// unchecked one returns `T` +fn same_functions_modulo_safety<'tcx>( + cx: &LateContext<'tcx>, + checked_def_id: DefId, + unchecked_def_id: DefId, + unwrapped_ret_ty: Ty<'tcx>, +) -> bool { + let hir_body = |def_id: DefId| -> Option<&'tcx Body<'tcx>> { cx.tcx.hir_maybe_body_owned_by(def_id.as_local()?) }; + let fn_sig = |def_id| cx.tcx.fn_sig(def_id).skip_binder().skip_binder(); + + if match (hir_body(checked_def_id), hir_body(unchecked_def_id)) { + // For local functions, we can get the parameter names. In that case, we want to make sure + // that the latter are equal between the checked and unchecked versions. + (Some(checked_body), Some(unchecked_body)) => { + over(checked_body.params, unchecked_body.params, |p1, p2| { + // We only allow simple params (plain bindings) for now, to stay on the safer side. + if let PatKind::Binding(bm1, _, ident1, None) = p1.pat.kind + && let PatKind::Binding(bm2, _, ident2, None) = p2.pat.kind + { + bm1 == bm2 && ident1 == ident2 + } else { + false + } + }) + }, + // For non-local functions, parameter names are not accessible. Oh well, we'll let it slip + (None, None) => true, + // If only one of the versions is non-local, then something weird happened. Bail just in case + _ => false, + } { + // Check that the functions have identical signatures, apart from safety, and return type (see + // below) + let checked_fn_sig = fn_sig(checked_def_id); + let unchecked_fn_sig = fn_sig(unchecked_def_id); + + (checked_fn_sig.safety() == Safety::Safe && unchecked_fn_sig.safety() == Safety::Unsafe) + && checked_fn_sig.c_variadic() == unchecked_fn_sig.c_variadic() + && checked_fn_sig.abi() == unchecked_fn_sig.abi() + // NOTE: the reason we use `same_type_modulo_regions` all over the place here is that + // the regions of different functions will be distinct, even if they are called the same + && over(checked_fn_sig.inputs(), unchecked_fn_sig.inputs(), |ty1, ty2| { + same_type_modulo_regions(*ty1, *ty2) + }) + // The checked version should return `Option` or `Result`, + // and the unchecked version should return just `T` + && same_type_modulo_regions(unchecked_fn_sig.output(), unwrapped_ret_ty) + && option_or_result_arg_ty(cx, checked_fn_sig.output()) + .is_some_and(|wrapped_ty| same_type_modulo_regions(wrapped_ty, unwrapped_ret_ty)) + // Check that the visibilities are the same (for the purposes of replacing, it would be enough to have + // the former _at least as_ visible as the latter, but we don't bother) + && cx.tcx.visibility(unchecked_def_id) == cx.tcx.visibility(checked_def_id) + } else { + false + } +} + +pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, recv: &Expr<'_>, call_span: Span) { + if expr.span.from_expansion() { + return; + } + let expected_ret_ty = cx.typeck_results().expr_ty(expr); + let (variant, checked_span, unchecked_sugg, unchecked_full_path) = match recv.kind { + // Construct `Variant::Fn(_)`, if applicable. This is necessary for us to handle + // functions like `std::str::from_utf8_unchecked`. + ExprKind::Call(path, _) + if let ExprKind::Path(qpath) = path.kind + && let checked_ident = last_path_segment(&qpath).ident + && let checked_def_id = path.res(cx).def_id() + && let Some((unchecked_def_id, unchecked_ident)) = + find_unchecked_sibling_fn(cx, checked_def_id, checked_ident) + && same_functions_modulo_safety(cx, checked_def_id, unchecked_def_id, expected_ret_ty) => + { + let unchecked_full_path = cx.tcx.def_path_str(unchecked_def_id); + ( + Variant::Fn, + checked_ident.span, + if checked_ident.span == path.span { + // replacing `bar(x)` with `bar_unchecked(x)` + // `bar_unchecked` might not be in scope, so suggest the full path + unchecked_full_path.clone() + } else { + // replacing `foo::bar(x)` with `foo::bar_unchecked(x)` + // since the path is qualified, we can just replace the final segment + unchecked_ident.to_string() + }, + unchecked_full_path, + ) + }, + // We unfortunately must handle `A::a(&a)` and `a.a()` separately, this handles the + // former + ExprKind::Call(path, _) + if let ExprKind::Path(qpath) = path.kind + && let checked_ident = last_path_segment(&qpath).ident + && let checked_def_id = path.res(cx).def_id() + && let Some((unchecked, unchecked_ident)) = + find_unchecked_sibling_method(cx, checked_def_id, checked_ident) + && let ty::AssocKind::Fn { has_self, .. } = unchecked.kind + && same_functions_modulo_safety(cx, checked_def_id, unchecked.def_id, expected_ret_ty) => + { + let unchecked_full_path = cx.tcx.def_path_str(unchecked.def_id); + ( + Variant::Assoc(AssocKind::new(has_self)), + // since this is basically a method call, we only need to replace the method ident + checked_ident.span, + unchecked_ident.to_string(), + unchecked_full_path, + ) + }, + // ... And now the latter ^^ + ExprKind::MethodCall(segment, _, _, _) + if let checked_ident = segment.ident + && let Some(checked_def_id) = cx.typeck_results().type_dependent_def_id(recv.hir_id) + && let Some((unchecked, unchecked_ident)) = + find_unchecked_sibling_method(cx, checked_def_id, checked_ident) + && same_functions_modulo_safety(cx, checked_def_id, unchecked.def_id, expected_ret_ty) => + { + let unchecked_full_path = cx.tcx.def_path_str(unchecked.def_id); + ( + Variant::Assoc(AssocKind::Method), + // since this is a method call, we only need to replace the method ident + checked_ident.span, + unchecked_ident.to_string(), + unchecked_full_path, + ) + }, + _ => return, + }; + + if !is_from_proc_macro(cx, expr) { + span_lint_and_then(cx, UNNECESSARY_UNWRAP_UNCHECKED, expr.span, variant.msg(), |diag| { + let sugg = vec![ + // replace the function with the unchecked version + (checked_span, unchecked_sugg), + // remove the call to `.unwrap_unchecked()` + (call_span.with_lo(recv.span.hi()), String::new()), + ]; + diag.multipart_suggestion( + format!("use `{unchecked_full_path}` instead, and remove the call to `.unwrap_unchecked()`"), + sugg, + // TODO: make this `MachineApplicable` when the function comes from std/alloc/core + // The reasoning is that, if the function comes from std/alloc/core, its checked and unchecked are + // pretty likely to have their semantics match. + Applicability::MaybeIncorrect, + ); + }); + } +} diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs index ba62853c74571..74da699e3c006 100644 --- a/src/tools/clippy/clippy_lints/src/minmax.rs +++ b/src/tools/clippy/clippy_lints/src/minmax.rs @@ -79,14 +79,15 @@ fn min_max<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(MinMax, Cons } }, ExprKind::MethodCall(path, receiver, args @ [_], _) => { + let m = match path.ident.name { + sym::max => MinMax::Max, + sym::min => MinMax::Min, + _ => return None, + }; if cx.typeck_results().expr_ty(receiver).is_floating_point() || cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::Ord) { - match path.ident.name { - sym::max => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Max), - sym::min => fetch_const(cx, expr.span.ctxt(), Some(receiver), args, MinMax::Min), - _ => None, - } + fetch_const(cx, expr.span.ctxt(), Some(receiver), args, m) } else { None } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs index 314004621ce05..79e56b7de41f8 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs @@ -333,11 +333,15 @@ impl EarlyLintPass for MiscEarlyLints { } fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { - if expr.span.in_external_macro(cx.sess().source_map()) { - return; - } - - if let ExprKind::Lit(lit) = expr.kind { + // `check_lit` only lints integer literals and suffixed float literals. + if let ExprKind::Lit(lit) = expr.kind + && match lit.kind { + token::LitKind::Integer => true, + token::LitKind::Float => lit.suffix.is_some(), + _ => false, + } + && !expr.span.in_external_macro(cx.sess().source_map()) + { MiscEarlyLints::check_lit(cx, lit, expr.span); } } diff --git a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs index 33ab94e00a5c6..5bc437cf24f4c 100644 --- a/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/misc_early/unneeded_field_pattern.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use itertools::Itertools; use rustc_ast::ast::{Pat, PatKind}; use rustc_lint::EarlyContext; @@ -59,7 +59,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) { .iter() .filter_map(|f| match f.pat.kind { PatKind::Wild => None, - _ => f.span.get_source_text(cx), + _ => f.span.get_text(cx), }) .format(", "), )); diff --git a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs index 7dbe39bb099d6..36a2a7f4b406c 100644 --- a/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs +++ b/src/tools/clippy/clippy_lints/src/missing_enforced_import_rename.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::paths::{PathNS, lookup_path_str}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefIdMap; @@ -78,7 +78,7 @@ impl LateLintPass<'_> for ImportRename { && let Some(name) = self.renames.get(&id) // Remove semicolon since it is not present for nested imports && let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';') - && let Some(snip) = span_without_semi.get_source_text(cx) + && let Some(snip) = span_without_semi.get_text(cx) && let Some(import) = match snip.split_once(" as ") { None => Some(snip.as_str()), Some((import, rename)) => { diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs index 634745422204f..657da09f5ca2b 100644 --- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs +++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs @@ -109,7 +109,7 @@ fn should_lint<'tcx>( // Is there a call to `DebugStruct::debug_struct`? Do lint if there is. let mut has_debug_struct = false; - for_each_expr(cx, block, |expr| { + for_each_expr(cx.tcx, block, |expr| { if let ExprKind::MethodCall(path, recv, ..) = &expr.kind { let recv_ty = typeck_results.expr_ty(recv).peel_refs(); @@ -166,7 +166,7 @@ fn check_struct<'tcx>( let mut has_direct_field_access = false; let mut field_accesses = FxHashSet::default(); - for_each_expr(cx, block, |expr| { + for_each_expr(cx.tcx, block, |expr| { if let ExprKind::Field(target, ident) = expr.kind && let target_ty = typeck_results.expr_ty_adjusted(target).peel_refs() && target_ty == self_ty diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs index 93cfed38c43ed..32a86db67c194 100644 --- a/src/tools/clippy/clippy_lints/src/missing_inline.rs +++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs @@ -1,8 +1,6 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_hir}; -use rustc_hir::def_id::DefId; -use rustc_hir::{self as hir, Attribute, find_attr}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::AssocContainer; +use clippy_utils::diagnostics::span_lint; +use rustc_hir::{ImplItem, ImplItemKind, Item, ItemKind, OwnerId, TraitFn, TraitItem, TraitItemKind, find_attr}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::config::CrateType; use rustc_session::declare_lint_pass; use rustc_span::Span; @@ -66,134 +64,41 @@ declare_clippy_lint! { declare_lint_pass!(MissingInline => [MISSING_INLINE_IN_PUBLIC_ITEMS]); -fn check_missing_inline_attrs( - cx: &LateContext<'_>, - attrs: &[Attribute], - sp: Span, - desc: &'static str, - hir_id: Option, -) { - if !find_attr!(attrs, Inline(..)) { - let msg = format!("missing `#[inline]` for {desc}"); - if let Some(hir_id) = hir_id { - span_lint_hir(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, hir_id, sp, msg); - } else { - span_lint(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, sp, msg); - } +fn check(cx: &LateContext<'_>, item: OwnerId, sp: Span) { + if cx.effective_visibilities.is_exported(item.def_id) + && !find_attr!(cx.tcx.hir_attrs(item.into()), Inline(..)) + // Rust `inline` doesn't mean anything with external linkage. + && !cx.tcx.codegen_fn_attrs(item.def_id).contains_extern_indicator() + && !cx.tcx.crate_types().iter().any(|&t| matches!(t, CrateType::ProcMacro)) + && !sp.in_external_macro(cx.tcx.sess.source_map()) + { + span_lint( + cx, + MISSING_INLINE_IN_PUBLIC_ITEMS, + sp, + "missing `#[inline]` on a publicly callable function", + ); } } impl<'tcx> LateLintPass<'tcx> for MissingInline { - fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx hir::Item<'_>) { - if it.span.in_external_macro(cx.sess().source_map()) { - return; - } - - if cx - .tcx - .crate_types() - .iter() - .any(|t: &CrateType| matches!(t, CrateType::ProcMacro)) - { - return; - } - - if !cx.effective_visibilities.is_exported(it.owner_id.def_id) { - return; - } - match it.kind { - hir::ItemKind::Fn { .. } => { - if fn_is_externally_exported(cx, it.owner_id.to_def_id()) { - return; - } - - let desc = "a function"; - let attrs = cx.tcx.hir_attrs(it.hir_id()); - check_missing_inline_attrs(cx, attrs, it.span, desc, None); - }, - hir::ItemKind::Trait { items: trait_items, .. } => { - // note: we need to check if the trait is exported so we can't use - // `LateLintPass::check_trait_item` here. - for &tit in trait_items { - let tit_ = cx.tcx.hir_trait_item(tit); - match tit_.kind { - hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => {}, - hir::TraitItemKind::Fn(..) => { - if cx.tcx.defaultness(tit.owner_id).has_value() { - // trait method with default body needs inline in case - // an impl is not provided - let desc = "a default trait method"; - let item = cx.tcx.hir_trait_item(tit); - let attrs = cx.tcx.hir_attrs(item.hir_id()); - check_missing_inline_attrs(cx, attrs, item.span, desc, Some(tit.hir_id())); - } - }, - } - } - }, - hir::ItemKind::Const(..) - | hir::ItemKind::Enum(..) - | hir::ItemKind::Macro(..) - | hir::ItemKind::Mod(..) - | hir::ItemKind::Static(..) - | hir::ItemKind::Struct(..) - | hir::ItemKind::TraitAlias(..) - | hir::ItemKind::GlobalAsm { .. } - | hir::ItemKind::TyAlias(..) - | hir::ItemKind::Union(..) - | hir::ItemKind::ExternCrate(..) - | hir::ItemKind::ForeignMod { .. } - | hir::ItemKind::Impl { .. } - | hir::ItemKind::Use(..) => {}, + fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) { + if let ItemKind::Fn { .. } = it.kind { + check(cx, it.owner_id, it.span); } } - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) { - if impl_item.span.in_external_macro(cx.sess().source_map()) - || cx - .tcx - .crate_types() - .iter() - .any(|t: &CrateType| matches!(t, CrateType::ProcMacro)) + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { + if let TraitItemKind::Fn(_, f) = item.kind + && let TraitFn::Provided(_) = f { - return; + check(cx, item.owner_id, item.span); } + } - // If the item being implemented is not exported, then we don't need #[inline] - if !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) { - return; - } - - let desc = match impl_item.kind { - hir::ImplItemKind::Fn(..) => "a method", - hir::ImplItemKind::Const(..) | hir::ImplItemKind::Type(_) => return, - }; - - let assoc_item = cx.tcx.associated_item(impl_item.owner_id); - let container_id = assoc_item.container_id(cx.tcx); - let trait_def_id = match assoc_item.container { - AssocContainer::Trait => Some(container_id), - AssocContainer::TraitImpl(_) => Some(cx.tcx.impl_trait_id(container_id)), - AssocContainer::InherentImpl => None, - }; - - if let Some(trait_def_id) = trait_def_id - && trait_def_id.is_local() - && !cx.effective_visibilities.is_exported(impl_item.owner_id.def_id) - { - // If a trait is being implemented for an item, and the - // trait is not exported, we don't need #[inline] - return; + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { + if let ImplItemKind::Fn(..) = item.kind { + check(cx, item.owner_id, item.span); } - - let attrs = cx.tcx.hir_attrs(impl_item.hir_id()); - check_missing_inline_attrs(cx, attrs, impl_item.span, desc, None); } } - -/// Checks if this function is externally exported, where #[inline] wouldn't have the desired effect -/// and a rustc warning would be triggered, see #15301 -fn fn_is_externally_exported(cx: &LateContext<'_>, def_id: DefId) -> bool { - let attrs = cx.tcx.codegen_fn_attrs(def_id); - attrs.contains_extern_indicator() -} diff --git a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs index 8b02c4865d138..a78f53188ba8b 100644 --- a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs +++ b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs @@ -6,7 +6,7 @@ use rustc_session::declare_lint_pass; use rustc_span::Span; use clippy_utils::diagnostics::span_lint; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; declare_clippy_lint! { /// ### What it does diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs index ad44d65b4d663..6d9950579fb24 100644 --- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs +++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeDef; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::ty_from_hir_ty; use rustc_errors::{Applicability, Diag}; diff --git a/src/tools/clippy/clippy_lints/src/needless_else.rs b/src/tools/clippy/clippy_lints/src/needless_else.rs index 807c4cf5da8e1..299860ac1e037 100644 --- a/src/tools/clippy/clippy_lints/src/needless_else.rs +++ b/src/tools/clippy/clippy_lints/src/needless_else.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{IntoSpan, SpanRangeExt}; +use clippy_utils::source::{IntoSpan, SpanExt}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass}; @@ -43,7 +43,7 @@ impl EarlyLintPass for NeedlessElse { && !else_clause.span.from_expansion() && block.stmts.is_empty() && let range = (then_block.span.hi()..expr.span.hi()).trim_start(cx) - && range.clone().check_source_text(cx, |src| { + && range.clone().check_text(cx, |src| { // Ignore else blocks that contain comments or #[cfg]s !src.contains(['/', '#']) }) diff --git a/src/tools/clippy/clippy_lints/src/needless_ifs.rs b/src/tools/clippy/clippy_lints/src/needless_ifs.rs index cdf9bd91339c6..48d15056f7291 100644 --- a/src/tools/clippy/clippy_lints/src/needless_ifs.rs +++ b/src/tools/clippy/clippy_lints/src/needless_ifs.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::higher::If; use clippy_utils::is_from_proc_macro; -use clippy_utils::source::{SpanRangeExt, walk_span_to_context}; +use clippy_utils::source::{SpanExt, walk_span_to_context}; use rustc_errors::Applicability; use rustc_hir::{ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -48,7 +48,7 @@ impl LateLintPass<'_> for NeedlessIfs { && block.stmts.is_empty() && block.expr.is_none() && !expr.span.in_external_macro(cx.sess().source_map()) - && then.span.check_source_text(cx, |src| { + && then.span.check_text(cx, |src| { // Ignore // - empty macro expansions // - empty reptitions in macro expansions @@ -58,7 +58,7 @@ impl LateLintPass<'_> for NeedlessIfs { .all(|ch| matches!(ch, b'{' | b'}') || ch.is_ascii_whitespace() || ch == b'\x0b') }) && let Some(cond_span) = walk_span_to_context(cond.span, expr.span.ctxt()) - && let Some(cond_snippet) = cond_span.get_source_text(cx) + && let Some(cond_snippet) = cond_span.get_text(cx) && !is_from_proc_macro(cx, expr) { span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs index bc4f6ca401710..69184d1894025 100644 --- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs +++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs @@ -1,16 +1,19 @@ +use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::{SourceText, SpanRangeExt, snippet}; +use clippy_utils::source::snippet_with_applicability; use clippy_utils::ty::needs_ordered_drop; use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used}; use core::ops::ControlFlow; use rustc_errors::{Applicability, MultiSpan}; use rustc_hir::{ - BindingMode, Block, Expr, ExprKind, HirId, LetStmt, LocalSource, MatchSource, Node, Pat, PatKind, Stmt, StmtKind, + BindingMode, Block, Expr, ExprKind, HirId, HirIdMap, HirIdSet, LetStmt, LocalSource, MatchSource, Node, Pat, + PatKind, Stmt, StmtKind, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_session::declare_lint_pass; +use rustc_session::impl_lint_pass; use rustc_span::Span; +use std::borrow::Cow; declare_clippy_lint! { /// ### What it does @@ -61,17 +64,126 @@ declare_clippy_lint! { "late initializations that can be replaced by a `let` statement with an initializer" } -declare_lint_pass!(NeedlessLateInit => [NEEDLESS_LATE_INIT]); +impl_lint_pass!(NeedlessLateInit<'_> => [NEEDLESS_LATE_INIT]); -fn contains_assign_expr<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> bool { - for_each_expr(cx, stmt, |e| { - if matches!(e.kind, ExprKind::Assign(..)) { - ControlFlow::Break(()) - } else { - ControlFlow::Continue(()) +pub struct NeedlessLateInit<'tcx> { + check_grouped_late_init: bool, + grouped_late_inits: Vec<(HirId, HirIdMap>)>, +} + +impl<'tcx> NeedlessLateInit<'tcx> { + pub fn new(conf: &'static Conf) -> Self { + Self { + check_grouped_late_init: conf.check_grouped_late_init, + grouped_late_inits: Vec::default(), } - }) - .is_some() + } + + fn check_if_or_match( + &mut self, + cx: &LateContext<'tcx>, + local_stmt: &'tcx LetStmt<'tcx>, + block: &'tcx Block<'tcx>, + binding_id: HirId, + usage: Usage<'tcx>, + exprs: impl IntoIterator>, + ) { + let mut assigns: Vec> = Vec::new(); + for expr in exprs { + let ty = cx.typeck_results().expr_ty(expr); + if ty.is_never() { + continue; + } + if !ty.is_unit() { + return; + } + + let Some(assign_group) = LocalAssignGroup::new(cx, expr) else { + return; + }; + + if let Some(last_group) = assigns.last() + && !assign_group.is_parallel(last_group) + { + return; + } + + assigns.push(assign_group); + } + + let Some(first_group) = assigns.first() else { + return; + }; + // If there are multiple assignments grouped together, lazyly check them after processing the block. + if first_group.0.len() > 1 { + if self.check_grouped_late_init { + let late_inits = if let Some((hir_id, late_inits)) = self.grouped_late_inits.last_mut() + && *hir_id == block.hir_id + { + late_inits + } else { + &mut self.grouped_late_inits.push_mut((block.hir_id, HirIdMap::default())).1 + }; + + let mut decls = HirIdMap::default(); + decls.insert(binding_id, local_stmt); + late_inits.insert(usage.expr.hir_id, GroupedLateInit { usage, assigns, decls }); + } + + return; + } + + if first_group.0[0].lhs_id == binding_id { + span_lint_and_then( + cx, + NEEDLESS_LATE_INIT, + local_stmt.span, + "unneeded late initialization", + |diag| { + let mut suggestions = vec![]; + for group in assigns { + suggestions.extend( + group + .0 + .iter() + .flat_map(|assign| { + let rhs_span = assign.rhs.span.source_callsite(); + let mut spans = vec![assign.span.until(rhs_span)]; + + if rhs_span.hi() != assign.span.hi() { + spans.push(rhs_span.shrink_to_hi().with_hi(assign.span.hi())); + } + + spans + }) + .map(|span| (span, String::new())), + ); + } + + suggestions.push((local_stmt.span, String::new())); + let mut applicability = Applicability::MachineApplicable; + let let_snippet = local_snippet_without_semicolon(cx, local_stmt, &mut applicability); + suggestions.push((usage.span.shrink_to_lo(), format!("{let_snippet} = "))); + if usage.needs_semi { + suggestions.push((usage.span.shrink_to_hi(), ";".to_owned())); + } + let binding_name = cx.tcx.hir_name(binding_id); + let descriptor = if matches!(usage.expr.kind, ExprKind::If(..)) { + "branches" + } else { + "`match` arms" + }; + diag.multipart_suggestion( + format!( + "move the declaration `{binding_name}` here and remove the assignments from the {descriptor}", + ), + suggestions, + applicability, + ); + }, + ); + } + } } fn contains_let(cond: &Expr<'_>) -> bool { @@ -99,112 +211,35 @@ fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool { } #[derive(Debug)] -struct LocalAssign { +struct LocalAssign<'tcx> { lhs_id: HirId, - rhs_span: Span, + rhs: &'tcx Expr<'tcx>, span: Span, } -impl LocalAssign { - fn from_expr(expr: &Expr<'_>, span: Span) -> Option { +impl<'tcx> LocalAssign<'tcx> { + fn new(expr: &'tcx Expr<'tcx>, span: Span) -> Option { if expr.span.from_expansion() { return None; } - if let ExprKind::Assign(lhs, rhs, _) = expr.kind { - if lhs.span.from_expansion() { - return None; - } - - Some(Self { + if let ExprKind::Assign(lhs, rhs, _) = expr.kind + && !lhs.span.from_expansion() + { + return Some(Self { lhs_id: lhs.res_local_id()?, - rhs_span: rhs.span.source_callsite(), + rhs, span, - }) - } else { - None - } - } - - fn new<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, binding_id: HirId) -> Option { - let assign = match expr.kind { - ExprKind::Block(Block { expr: Some(expr), .. }, _) => Self::from_expr(expr, expr.span), - ExprKind::Block(block, _) => { - if let Some((last, other_stmts)) = block.stmts.split_last() - && let StmtKind::Expr(expr) | StmtKind::Semi(expr) = last.kind - - && let assign = Self::from_expr(expr, last.span)? - - // avoid visiting if not needed - && assign.lhs_id == binding_id - && other_stmts.iter().all(|stmt| !contains_assign_expr(cx, stmt)) - { - Some(assign) - } else { - None - } - }, - ExprKind::Assign(..) => Self::from_expr(expr, expr.span), - _ => None, - }?; - - if assign.lhs_id == binding_id { - Some(assign) - } else { - None - } - } -} - -fn assignment_suggestions<'tcx>( - cx: &LateContext<'tcx>, - binding_id: HirId, - exprs: impl IntoIterator>, -) -> Option<(Applicability, Vec<(Span, String)>)> { - let mut assignments = Vec::new(); - - for expr in exprs { - let ty = cx.typeck_results().expr_ty(expr); - - if ty.is_never() { - continue; - } - if !ty.is_unit() { - return None; + }); } - let assign = LocalAssign::new(cx, expr, binding_id)?; - - assignments.push(assign); - } - - let suggestions = assignments - .iter() - .flat_map(|assignment| { - let mut spans = vec![assignment.span.until(assignment.rhs_span)]; - - if assignment.rhs_span.hi() != assignment.span.hi() { - spans.push(assignment.rhs_span.shrink_to_hi().with_hi(assignment.span.hi())); - } - - spans - }) - .map(|span| (span, String::new())) - .collect::>(); - - match suggestions.len() { - // All of `exprs` are never types - // https://github.com/rust-lang/rust-clippy/issues/8911 - 0 => None, - 1 => Some((Applicability::MachineApplicable, suggestions)), - // multiple suggestions don't work with rustfix in multipart_suggest - // https://github.com/rust-lang/rustfix/issues/141 - _ => Some((Applicability::Unspecified, suggestions)), + None } } +#[derive(Debug)] struct Usage<'tcx> { - stmt: &'tcx Stmt<'tcx>, + span: Span, expr: &'tcx Expr<'tcx>, needs_semi: bool, } @@ -226,20 +261,34 @@ fn first_usage<'tcx>( .find(|&stmt| is_local_used(cx, stmt, binding_id)) .and_then(|stmt| match stmt.kind { StmtKind::Expr(expr) => Some(Usage { - stmt, + span: stmt.span, expr, needs_semi: true, }), StmtKind::Semi(expr) => Some(Usage { - stmt, + span: stmt.span, expr, needs_semi: false, }), _ => None, }) + .or_else(|| { + block + .expr + .filter(|expr| is_local_used(cx, *expr, binding_id)) + .map(|expr| Usage { + span: expr.span, + expr, + needs_semi: true, + }) + }) } -fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> Option { +fn local_snippet_without_semicolon<'a>( + cx: &LateContext<'_>, + local: &LetStmt<'_>, + applicability: &mut Applicability, +) -> Cow<'a, str> { let span = local.span.with_hi(match local.ty { // let : ; // ~~~~~~~~~~~~~~~ @@ -249,105 +298,70 @@ fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> None => local.pat.span.hi(), }); - span.get_source_text(cx) + snippet_with_applicability(cx, span, "..", applicability) } -fn check<'tcx>( - cx: &LateContext<'tcx>, - local: &'tcx LetStmt<'tcx>, - local_stmt: &'tcx Stmt<'tcx>, - block: &'tcx Block<'tcx>, - binding_id: HirId, -) -> Option<()> { - let usage = first_usage(cx, binding_id, local_stmt.hir_id, block)?; - let binding_name = cx.tcx.hir_opt_name(binding_id)?; - let let_snippet = local_snippet_without_semicolon(cx, local)?; - - match usage.expr.kind { - ExprKind::Assign(..) => { - let assign = LocalAssign::new(cx, usage.expr, binding_id)?; - let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]); - msg_span.push_span_label(local_stmt.span, "created here"); - msg_span.push_span_label(assign.span, "initialised here"); - - span_lint_and_then( - cx, - NEEDLESS_LATE_INIT, - msg_span, - "unneeded late initialization", - |diag| { - diag.multipart_suggestion( - format!("move the declaration `{binding_name}` here"), - vec![ - (local_stmt.span, String::new()), - ( - assign.span, - let_snippet.to_owned() + " = " + &snippet(cx, assign.rhs_span, ".."), - ), - ], - Applicability::MachineApplicable, - ); - }, - ); - }, - ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => { - let (applicability, mut suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?; - - span_lint_and_then( - cx, - NEEDLESS_LATE_INIT, - local_stmt.span, - "unneeded late initialization", - |diag| { - suggestions.push((local_stmt.span, String::new())); - suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = "))); - - if usage.needs_semi { - suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned())); +#[derive(Debug)] +struct LocalAssignGroup<'tcx>(Vec>); + +impl<'tcx> LocalAssignGroup<'tcx> { + fn new(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option { + match expr.kind { + ExprKind::Block(Block { expr: Some(expr), .. }, _) + if let Some(assign) = LocalAssign::new(expr, expr.span) => + { + Some(LocalAssignGroup(vec![assign])) + }, + ExprKind::Block(Block { expr: None, stmts, .. }, _) => { + let mut assign_group = Vec::new(); + // Avoid cases when the assignee is used or reassigned in the subsequent assignments + let mut used_locals = HirIdSet::default(); + for stmt in stmts.iter().rev() { + if let StmtKind::Semi(expr) | StmtKind::Expr(expr) = stmt.kind + && let Some(assign) = LocalAssign::new(expr, stmt.span) + && !used_locals.contains(&assign.lhs_id) + { + used_locals.insert(assign.lhs_id); + for_each_expr(cx.tcx, assign.rhs, |e| { + if let Some(id) = e.res_local_id() { + used_locals.insert(id); + } + ControlFlow::<()>::Continue(()) + }); + assign_group.push(assign); + continue; } - diag.multipart_suggestion( - format!( - "move the declaration `{binding_name}` here and remove the assignments from the branches" - ), - suggestions, - applicability, - ); - }, - ); - }, - ExprKind::Match(_, arms, MatchSource::Normal) => { - let (applicability, mut suggestions) = - assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?; - - span_lint_and_then( - cx, - NEEDLESS_LATE_INIT, - local_stmt.span, - "unneeded late initialization", - |diag| { - suggestions.push((local_stmt.span, String::new())); - suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = "))); - - if usage.needs_semi { - suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned())); - } + break; + } + if assign_group.is_empty() { + None + } else { + Some(LocalAssignGroup(assign_group)) + } + }, + ExprKind::Assign(..) if let Some(assign) = LocalAssign::new(expr, expr.span) => { + Some(LocalAssignGroup(vec![assign])) + }, + _ => None, + } + } - diag.multipart_suggestion( - format!("move the declaration `{binding_name}` here and remove the assignments from the `match` arms"), - suggestions, - applicability, - ); - }, - ); - }, - _ => {}, + /// Checks if the assignments in `self` and `other` are parallel, i.e. they have the same number + /// of assignments and the same assignees in the same order. + fn is_parallel(&self, other: &Self) -> bool { + self.0.len() == other.0.len() && self.0.iter().zip(other.0.iter()).all(|(a, b)| a.lhs_id == b.lhs_id) } +} - Some(()) +#[derive(Debug)] +struct GroupedLateInit<'tcx> { + usage: Usage<'tcx>, + assigns: Vec>, + decls: HirIdMap<&'tcx LetStmt<'tcx>>, } -impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { +impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit<'tcx> { fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) { let mut parents = cx.tcx.hir_parent_iter(local.hir_id); if let LetStmt { @@ -362,8 +376,147 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit { } = local && let Some((_, Node::Stmt(local_stmt))) = parents.next() && let Some((_, Node::Block(block))) = parents.next() + && let Some(usage) = first_usage(cx, *binding_id, local_stmt.hir_id, block) { - check(cx, local, local_stmt, block, *binding_id); + if self.check_grouped_late_init + && let Some((hir_id, late_inits)) = self.grouped_late_inits.last_mut() + && *hir_id == block.hir_id + && let Some(late_init) = late_inits.get_mut(&usage.expr.hir_id) + { + late_init.decls.insert(*binding_id, local); + return; + } + + match usage.expr.kind { + ExprKind::Assign(..) + if let Some(assign) = LocalAssign::new(usage.expr, usage.expr.span) + && assign.lhs_id == *binding_id => + { + let mut applicability = Applicability::MachineApplicable; + let let_snippet = local_snippet_without_semicolon(cx, local, &mut applicability); + let binding_name = cx.tcx.hir_name(*binding_id); + let mut msg_span = MultiSpan::from_spans(vec![local_stmt.span, assign.span]); + msg_span.push_span_label(local_stmt.span, "created here"); + msg_span.push_span_label(assign.span, "initialised here"); + + span_lint_and_then( + cx, + NEEDLESS_LATE_INIT, + msg_span, + "unneeded late initialization", + |diag| { + let mut applicability = Applicability::MachineApplicable; + let rhs_snippet = snippet_with_applicability( + cx, + assign.rhs.span.source_callsite(), + "..", + &mut applicability, + ); + diag.multipart_suggestion( + format!("move the declaration `{binding_name}` here"), + vec![ + (local_stmt.span, String::new()), + (assign.span, format!("{let_snippet} = {rhs_snippet}")), + ], + applicability, + ); + }, + ); + }, + ExprKind::If(cond, then_expr, Some(mut else_expr)) if !contains_let(cond) => { + // Flatten multiple if branches + let mut exprs = vec![then_expr]; + while let ExprKind::If(cond, then, Some(else_)) = else_expr.kind { + if contains_let(cond) { + return; + } + exprs.push(then); + else_expr = else_; + } + exprs.push(else_expr); + self.check_if_or_match(cx, local, block, *binding_id, usage, exprs); + }, + ExprKind::Match(_, arms, MatchSource::Normal) => { + self.check_if_or_match(cx, local, block, *binding_id, usage, arms.iter().map(|arm| arm.body)); + }, + _ => {}, + } + } + } + + fn check_block_post(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { + if self.check_grouped_late_init + && let Some((_, late_inits)) = self.grouped_late_inits.pop_if(|(hir_id, _)| *hir_id == block.hir_id) + { + 'outer: for (_, late_init) in late_inits { + if late_init.decls.len() < late_init.assigns[0].0.len() { + continue; + } + + let mut suggestions = vec![]; + for assign in &late_init.assigns[0].0 { + if let Some(local) = late_init.decls.get(&assign.lhs_id) + // If the local has a type annotation, skip it since removing the annotation might cause type + // inference issues while annotating the tuple makes the suggestion harder to read. + && local.ty.is_none() + { + suggestions.push((local.span, String::new())); + } else { + continue 'outer; + } + } + + span_lint_and_then( + cx, + NEEDLESS_LATE_INIT, + late_init.usage.span, + "unneeded late initialization", + |diag| { + let mut applicability = Applicability::MachineApplicable; + for group in &late_init.assigns { + let rhs_snippet = group + .0 + .iter() + .rev() + .map(|assign| { + snippet_with_applicability( + cx, + assign.rhs.span.source_callsite(), + "..", + &mut applicability, + ) + }) + .intersperse(", ".into()) + .collect::(); + suggestions.push(( + group.0.last().unwrap().span.to(group.0[0].span), + format!("({rhs_snippet})"), + )); + } + let let_snippet = late_init.assigns[0] + .0 + .iter() + .rev() + .map(|assign| cx.tcx.hir_name(assign.lhs_id).to_string()) + .intersperse(", ".to_owned()) + .collect::(); + suggestions.push((late_init.usage.span.shrink_to_lo(), format!("let ({let_snippet}) = "))); + if late_init.usage.needs_semi { + suggestions.push((late_init.usage.span.shrink_to_hi(), ";".to_owned())); + } + let descriptor = if matches!(late_init.usage.expr.kind, ExprKind::If(..)) { + "branches" + } else { + "`match` arms" + }; + diag.multipart_suggestion( + format!("move the declarations here and remove the assignments from the {descriptor}"), + suggestions, + applicability, + ); + }, + ); + } } } } diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs index 91358ef77fa1e..0d5b2ee6ba29e 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs @@ -1,7 +1,6 @@ use super::needless_pass_by_value::requires_exact_signature; use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_hir_and_then; -use clippy_utils::source::HasSession as _; use clippy_utils::visitors::for_each_expr; use clippy_utils::{inherits_cfg, is_from_proc_macro, is_self}; use core::ops::ControlFlow; @@ -207,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { // We retrieve all the closures declared in the function because they will not be found // by `euv::Delegate`. let mut closures: FxIndexSet = FxIndexSet::default(); - for_each_expr(cx, body, |expr| { + for_each_expr(cx.tcx, body, |expr| { if let ExprKind::Closure(closure) = expr.kind { closures.insert(closure.def_id); } @@ -269,7 +268,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> { // If the argument is never used mutably, we emit the warning. let sp = input.span; if let rustc_hir::TyKind::Ref(_, inner_ty) = input.kind { - let Some(after_mut_span) = cx.sess().source_map().span_extend_to_prev_str( + let Some(after_mut_span) = cx.tcx.sess.source_map().span_extend_to_prev_str( inner_ty.ty.span.shrink_to_lo(), "mut", true, diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs index 4ff5b0b0b3c39..a2ad531751288 100644 --- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs +++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::{SpanRangeExt, snippet}; +use clippy_utils::source::{SpanExt, snippet}; use clippy_utils::ty::{implements_trait, implements_trait_with_env_from_iter, is_copy}; use clippy_utils::visitors::{Descend, for_each_expr_without_closures}; use clippy_utils::{is_self, peel_hir_ty_options, strip_pat_refs, sym}; @@ -248,7 +248,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { for (span, suggestion) in clone_spans { diag.span_suggestion( span, - span.get_source_text(cx).map_or_else( + span.get_text(cx).map_or_else( || "change the call to".to_owned(), |src| format!("change `{src}` to"), ), @@ -275,7 +275,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue { for (span, suggestion) in clone_spans { diag.span_suggestion( span, - span.get_source_text(cx).map_or_else( + span.get_text(cx).map_or_else( || "change the call to".to_owned(), |src| format!("change `{src}` to"), ), diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs index 9e6fc68116d4f..933ad716446f2 100644 --- a/src/tools/clippy/clippy_lints/src/no_effect.rs +++ b/src/tools/clippy/clippy_lints/src/no_effect.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::{expr_type_is_certain, has_drop}; use clippy_utils::{in_automatically_derived, is_inside_always_const_context, is_lint_allowed, peel_blocks}; use rustc_errors::Applicability; @@ -278,8 +278,8 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { if let ExprKind::Index(..) = &expr.kind { if !is_inside_always_const_context(cx.tcx, expr.hir_id) && let [arr, func] = &*reduced - && let Some(arr) = arr.span.get_source_text(cx) - && let Some(func) = func.span.get_source_text(cx) + && let Some(arr) = arr.span.get_text(cx) + && let Some(func) = func.span.get_text(cx) { span_lint_hir_and_then( cx, @@ -300,7 +300,7 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) { } else { let mut snippet = String::new(); for e in reduced { - if let Some(snip) = e.span.get_source_text(cx) { + if let Some(snip) = e.span.get_text(cx) { snippet.push_str(&snip); snippet.push_str("; "); } else { diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs index cb934466bd890..cf19cd9fc81d5 100644 --- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs +++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SpanRangeExt, snippet_with_applicability}; +use clippy_utils::source::{SpanExt, snippet_with_applicability}; use clippy_utils::sym; use rustc_errors::Applicability; use rustc_hir::{Expr, ExprKind}; @@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { && param.span.eq_ctxt(expr.span) && param .span - .check_source_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..])) + .check_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..])) { show_error(cx, param); } @@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions { && param.span.eq_ctxt(expr.span) && param .span - .check_source_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..])) + .check_text(cx, |src| !matches!(src.as_bytes(), [b'0', b'o' | b'b', ..])) { show_error(cx, param); } diff --git a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs index 487eaecf18890..2755e3add760e 100644 --- a/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs +++ b/src/tools/clippy/clippy_lints/src/non_std_lazy_statics.rs @@ -198,7 +198,7 @@ impl LazyInfo { // visit body to collect `Lazy::new` calls let mut new_fn_calls = FxIndexMap::default(); - for_each_expr::<(), ()>(cx, body, |ex| { + for_each_expr::<(), ()>(cx.tcx, body, |ex| { if let Some((fn_did, call_span)) = fn_def_id_and_span_from_body(cx, ex, body_id) && paths::ONCE_CELL_SYNC_LAZY_NEW.matches(cx, fn_did) { diff --git a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs index b449681ae2e14..e601d54cc4eda 100644 --- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs +++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_config::types::MacroMatcher; use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SourceText, SpanRangeExt}; +use clippy_utils::source::{SourceText, SpanExt}; use rustc_ast::ast; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_errors::Applicability; @@ -129,7 +129,7 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace if let ExpnKind::Macro(MacroKind::Bang, mac_name) = expn_data.kind && let name = mac_name.as_str() && let Some(&braces) = mac_braces.macro_braces.get(name) - && let Some(snip) = expn_data.call_site.get_source_text(cx) + && let Some(snip) = expn_data.call_site.get_text(cx) // we must check only invocation sites // https://github.com/rust-lang/rust-clippy/issues/7422 && let Some(macro_args_str) = snip.strip_prefix(name).and_then(|snip| snip.strip_prefix('!')) diff --git a/src/tools/clippy/clippy_lints/src/octal_escapes.rs b/src/tools/clippy/clippy_lints/src/octal_escapes.rs index 6e7ee727965db..5735c23a62ac8 100644 --- a/src/tools/clippy/clippy_lints/src/octal_escapes.rs +++ b/src/tools/clippy/clippy_lints/src/octal_escapes.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::token::LitKind; use rustc_ast::{Expr, ExprKind}; use rustc_errors::Applicability; @@ -86,7 +86,7 @@ impl EarlyLintPass for OctalEscapes { // Last check to make sure the source text matches what we read from the string. // Macros are involved somehow if this doesn't match. - if span.check_source_text(cx, |src| match *src.as_bytes() { + if span.check_text(cx, |src| match *src.as_bytes() { [b'\\', b'0', lo] => lo == c_lo, [b'\\', b'0', hi, lo] => hi == c_hi && lo == c_lo, _ => false, diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs index 5695779425f4a..fa391fad589fe 100644 --- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs +++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::msrvs::Msrv; use clippy_utils::qualify_min_const_fn::is_stable_const_fn; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::implements_trait; use clippy_utils::visitors::for_each_expr_without_closures; use clippy_utils::{binop_traits, eq_expr_value, is_in_const_context, trait_ref_of_method}; @@ -74,8 +74,8 @@ pub(super) fn check<'tcx>( expr.span, "manual implementation of an assign operation", |diag| { - if let Some(snip_a) = assignee.span.get_source_text(cx) - && let Some(snip_r) = rhs.span.get_source_text(cx) + if let Some(snip_a) = assignee.span.get_text(cx) + && let Some(snip_r) = rhs.span.get_text(cx) { diag.span_suggestion( expr.span, diff --git a/src/tools/clippy/clippy_lints/src/operators/decimal_bitwise_operands.rs b/src/tools/clippy/clippy_lints/src/operators/decimal_bitwise_operands.rs index 8511f2151342d..08e56e4516c52 100644 --- a/src/tools/clippy/clippy_lints/src/operators/decimal_bitwise_operands.rs +++ b/src/tools/clippy/clippy_lints/src/operators/decimal_bitwise_operands.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_help; use clippy_utils::numeric_literal; use clippy_utils::numeric_literal::NumericLiteral; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::LitKind; use rustc_data_structures::packed::Pu128; use rustc_hir::{BinOpKind, Expr, ExprKind}; @@ -40,7 +40,7 @@ fn check_expr(cx: &LateContext<'_>, expr: &Expr<'_>) { if let LitKind::Int(Pu128(val), _) = lit.node && !is_single_digit(val) && !is_power_of_twoish(val) - && let Some(src) = lit.span.get_source_text(cx) + && let Some(src) = lit.span.get_text(cx) && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) && num_lit.is_decimal() { diff --git a/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs b/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs index f0b6407a141bd..8a2cdf430927b 100644 --- a/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs +++ b/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{eq_expr_value, sugg}; use rustc_errors::Applicability; use rustc_hir as hir; @@ -43,8 +43,8 @@ fn lint_misrefactored_assign_op( expr.span, "variable appears on both sides of an assignment operation", |diag| { - if let Some(snip_a) = assignee.span.get_source_text(cx) - && let Some(snip_r) = rhs_other.span.get_source_text(cx) + if let Some(snip_a) = assignee.span.get_text(cx) + && let Some(snip_r) = rhs_other.span.get_text(cx) { let a = &sugg::Sugg::hir(cx, assignee, ".."); let r = &sugg::Sugg::hir(cx, rhs, ".."); diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs index cc147a4a84bdc..fa7a5d8feb380 100644 --- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs +++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs @@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicInResultFn { fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, body: &'tcx hir::Body<'tcx>) { let mut panics = Vec::new(); - let _: Option = for_each_expr(cx, body.value, |e| { + let _: Option = for_each_expr(cx.tcx, body.value, |e| { let Some(macro_call) = root_macro_call_first_node(cx, e) else { return ControlFlow::Continue(Descend::Yes); }; diff --git a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs index a5e57d97301e3..473718c5419f1 100644 --- a/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs +++ b/src/tools/clippy/clippy_lints/src/pathbuf_init_then_push.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_sugg; use clippy_utils::res::{MaybeDef, MaybeResPath}; -use clippy_utils::source::{SpanRangeExt, snippet}; +use clippy_utils::source::{SpanExt, snippet}; use clippy_utils::sym; use rustc_ast::{LitKind, StrStyle}; use rustc_errors::Applicability; @@ -73,7 +73,7 @@ impl PathbufPushSearcher<'_> { && let Some(arg) = self.arg && let ExprKind::Lit(x) = arg.kind && let LitKind::Str(_, StrStyle::Cooked) = x.node - && let Some(s) = arg.span.get_source_text(cx) + && let Some(s) = arg.span.get_text(cx) { Some(format!(" = PathBuf::from({s});")) } else { @@ -83,8 +83,8 @@ impl PathbufPushSearcher<'_> { fn gen_pathbuf_join(&self, cx: &LateContext<'_>) -> Option { let arg = self.arg?; - let arg_str = arg.span.get_source_text(cx)?; - let init_val = self.init_val.span.get_source_text(cx)?; + let arg_str = arg.span.get_text(cx)?; + let init_val = self.init_val.span.get_text(cx)?; Some(format!(" = {init_val}.join({arg_str});")) } diff --git a/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs b/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs index 40bc42dcdcb36..200ab8f8418f1 100644 --- a/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs +++ b/src/tools/clippy/clippy_lints/src/ptr/ptr_arg.rs @@ -1,7 +1,7 @@ use super::PTR_ARG; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::{VEC_METHODS_SHADOWING_SLICE_METHODS, get_expr_use_or_unification_node, is_lint_allowed, sym}; use hir::LifetimeKind; use rustc_abi::ExternAbi; @@ -55,7 +55,7 @@ pub(super) fn check_body<'tcx>( .chain(result.replacements.iter().map(|r| { ( r.expr_span, - format!("{}{}", r.self_span.get_source_text(cx).unwrap(), r.replacement), + format!("{}{}", r.self_span.get_text(cx).unwrap(), r.replacement), ) })) .collect(), @@ -156,7 +156,7 @@ impl fmt::Display for DerefTyDisplay<'_, '_> { DerefTy::Path => f.write_str("Path"), DerefTy::Slice(hir_ty, ty) => { f.write_char('[')?; - match hir_ty.and_then(|s| s.get_source_text(self.0)) { + match hir_ty.and_then(|s| s.get_text(self.0)) { Some(s) => f.write_str(&s)?, None => ty.fmt(f)?, } @@ -279,7 +279,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>( diag.span_suggestion( hir_ty.span, "change this to", - match ty.span().get_source_text(cx) { + match ty.span().get_text(cx) { Some(s) => format!("&{}{s}", mutability.prefix_str()), None => format!("&{}{}", mutability.prefix_str(), args.type_at(1)), }, diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs index cbfd1af1907b7..7f95adba870d7 100644 --- a/src/tools/clippy/clippy_lints/src/question_mark.rs +++ b/src/tools/clippy/clippy_lints/src/question_mark.rs @@ -629,6 +629,11 @@ fn is_inferred_ret_closure(expr: &Expr<'_>) -> bool { impl<'tcx> LateLintPass<'tcx> for QuestionMark { fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) { + // Cheap `let` check before the costlier lint level and const context queries. + if !matches!(stmt.kind, StmtKind::Let(..)) { + return; + } + if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) || !self.msrv.meets(cx, msrvs::QUESTION_MARK_OPERATOR) { return; @@ -646,7 +651,9 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark { return; } - if !self.inside_try_block() + // Cheap `if`/`match` check before the costlier lint level and const context queries. + if matches!(expr.kind, ExprKind::If(..) | ExprKind::Match(..)) + && !self.inside_try_block() && !is_in_const_context(cx) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id) && self.msrv.meets(cx, msrvs::QUESTION_MARK_OPERATOR) diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs index 71965ee1e29f9..2fe368c0639ab 100644 --- a/src/tools/clippy/clippy_lints/src/ranges.rs +++ b/src/tools/clippy/clippy_lints/src/ranges.rs @@ -3,7 +3,7 @@ use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; use clippy_utils::res::MaybeResPath; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; +use clippy_utils::source::{SpanExt, snippet, snippet_with_applicability}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::implements_trait; use clippy_utils::{ @@ -191,14 +191,21 @@ impl Ranges { impl<'tcx> LateLintPass<'tcx> for Ranges { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if let ExprKind::Binary(ref op, l, r) = expr.kind + && matches!( + op.node, + BinOpKind::And | BinOpKind::BitAnd | BinOpKind::Or | BinOpKind::BitOr + ) && self.msrv.meets(cx, msrvs::RANGE_CONTAINS) + && !is_in_const_context(cx) { check_possible_range_contains(cx, op.node, l, r, expr, expr.span); } - check_exclusive_range_plus_one(cx, expr); - check_inclusive_range_minus_one(cx, expr); - check_reversed_empty_range(cx, expr); + if let Some(range) = higher::Range::hir(cx, expr) { + check_exclusive_range_plus_one(cx, expr, &range); + check_inclusive_range_minus_one(cx, expr, &range); + check_reversed_empty_range(cx, expr, &range); + } } } @@ -210,10 +217,6 @@ fn check_possible_range_contains( expr: &Expr<'_>, span: Span, ) { - if is_in_const_context(cx) { - return; - } - let combine_and = match op { BinOpKind::And | BinOpKind::BitAnd => true, BinOpKind::Or | BinOpKind::BitOr => false, @@ -300,7 +303,7 @@ fn check_possible_range_contains( if let ExprKind::Binary(ref lhs_op, _left, new_lhs) = left.kind && op == lhs_op.node && let new_span = Span::new(new_lhs.span.lo(), right.span.hi(), expr.span.ctxt(), expr.span.parent()) - && new_span.check_source_text(cx, |src| { + && new_span.check_text(cx, |src| { // Do not continue if we have mismatched number of parens, otherwise the suggestion is wrong src.matches('(').count() == src.matches(')').count() }) @@ -481,54 +484,58 @@ fn can_switch_ranges<'tcx>( } // exclusive range plus one: `x..(y+1)` -fn check_exclusive_range_plus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +fn check_exclusive_range_plus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, range: &higher::Range<'tcx>) { check_range_switch( cx, expr, + range, RangeLimits::HalfOpen, y_plus_one, RANGE_PLUS_ONE, "an inclusive range would be more readable", - "..=", ); } // inclusive range minus one: `x..=(y-1)` -fn check_inclusive_range_minus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { +fn check_inclusive_range_minus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, range: &higher::Range<'tcx>) { check_range_switch( cx, expr, + range, RangeLimits::Closed, y_minus_one, RANGE_MINUS_ONE, "an exclusive range would be more readable", - "..", ); } /// Check for a `kind` of range in `expr`, check for `predicate` on the end, -/// and emit the `lint` with `msg` and the `operator`. +/// and emit the `lint` with `msg`, suggesting the opposite range limits. fn check_range_switch<'tcx>( cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, + range: &higher::Range<'tcx>, kind: RangeLimits, predicate: impl for<'hir> FnOnce(&Expr<'hir>) -> Option<&'hir Expr<'hir>>, lint: &'static Lint, msg: &'static str, - operator: &str, ) { - if let Some(range) = higher::Range::hir(cx, expr) - && let higher::Range { - start, - end: Some(end), - limits, - span, - } = range + if let higher::Range { + start, + end: Some(end), + limits, + span, + } = *range && span.can_be_used_for_suggestions() && limits == kind && let Some(y) = predicate(end) && can_switch_ranges(cx, span.ctxt(), expr, kind, cx.typeck_results().expr_ty(y)) { + // Suggest the opposite range limits to the ones being checked. + let operator = match kind { + RangeLimits::HalfOpen => "..=", + RangeLimits::Closed => "..", + }; span_lint_and_then(cx, lint, span, msg, |diag| { let mut app = Applicability::MachineApplicable; let start = start.map_or(String::new(), |x| { @@ -550,7 +557,7 @@ fn check_range_switch<'tcx>( } } -fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { +fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>, range: &higher::Range<'_>) { fn inside_indexing_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool { matches!( get_parent_expr(cx, expr), @@ -580,12 +587,12 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) { } } - if let Some(higher::Range { + if let higher::Range { start: Some(start), end: Some(end), limits, span, - }) = higher::Range::hir(cx, expr) + } = *range && let ty = cx.typeck_results().expr_ty(start) && let ty::Int(_) | ty::Uint(_) = ty.kind() && let ecx = ConstEvalCtxt::new(cx) diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs index d8aa88d48b09d..31c90c591b7c2 100644 --- a/src/tools/clippy/clippy_lints/src/raw_strings.rs +++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs @@ -1,6 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, snippet_opt}; +use clippy_utils::source::{SpanExt, snippet_opt}; use rustc_ast::ast::{Expr, ExprKind}; use rustc_ast::token::LitKind; use rustc_errors::Applicability; @@ -74,7 +74,7 @@ impl EarlyLintPass for RawStrings { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::FormatArgs(format_args) = &expr.kind && !format_args.span.in_external_macro(cx.sess().source_map()) - && format_args.span.check_source_text(cx, |src| src.starts_with('r')) + && format_args.span.check_text(cx, |src| src.starts_with('r')) && let Some(str) = snippet_opt(cx.sess(), format_args.span) && let count_hash = str.bytes().skip(1).take_while(|b| *b == b'#').count() && let Some(str) = str.get(count_hash + 2..str.len() - count_hash - 1) @@ -97,7 +97,7 @@ impl EarlyLintPass for RawStrings { _ => return, } && !expr.span.in_external_macro(cx.sess().source_map()) - && expr.span.check_source_text(cx, |src| src.starts_with(prefix)) + && expr.span.check_text(cx, |src| src.starts_with(prefix)) { self.check_raw_string(cx, lit.symbol.as_str(), expr.span, prefix, max, lit.kind.descr()); } diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs index bfb704dd21719..db3be9e64f147 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then}; use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage}; use clippy_utils::res::MaybeDef; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::{has_drop, is_copy, peel_and_count_ty_refs}; use clippy_utils::{fn_has_unsatisfiable_preds, sym}; use rustc_errors::Applicability; @@ -214,7 +214,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone { .unwrap_crate_local() .lint_root; - if let Some(snip) = span.get_source_text(cx) + if let Some(snip) = span.get_text(cx) && let Some(dot) = snip.rfind('.') { let sugg_span = span.with_lo(span.lo() + BytePos(u32::try_from(dot).unwrap())); diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs index 7ebf7ccb7571e..c7f7b47403376 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs @@ -1,5 +1,4 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::HasSession; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{Item, ItemKind, UseKind}; @@ -49,7 +48,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { && !cx.effective_visibilities.is_exported(item.owner_id.def_id) && self.is_exported.last() == Some(&false) && !is_ignorable_export(item) - && !item.span.in_external_macro(cx.sess().source_map()) + && !item.span.in_external_macro(cx.tcx.sess.source_map()) { let span = item .kind diff --git a/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs b/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs index 602093259eaec..2dc03328fef86 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_test_prefix.rs @@ -144,7 +144,7 @@ fn name_conflicts<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, fn_name: S // Also check that within the body of the function there is also no function call // with the same name (since it will result in recursion) - for_each_expr(cx, body, |expr| { + for_each_expr(cx.tcx, body, |expr| { if let ExprKind::Path(qpath) = &expr.kind && let Some(def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id() && let Some(name) = tcx.opt_item_name(def_id) diff --git a/src/tools/clippy/clippy_lints/src/ref_patterns.rs b/src/tools/clippy/clippy_lints/src/ref_patterns.rs index b6a28571230a6..22d1c21aa43ba 100644 --- a/src/tools/clippy/clippy_lints/src/ref_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/ref_patterns.rs @@ -1,6 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; -use rustc_ast::ast::{BindingMode, Pat, PatKind}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use clippy_utils::in_automatically_derived; +use rustc_hir::{BindingMode, Pat, PatKind}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::declare_lint_pass; declare_clippy_lint! { @@ -29,10 +30,11 @@ declare_clippy_lint! { declare_lint_pass!(RefPatterns => [REF_PATTERNS]); -impl EarlyLintPass for RefPatterns { - fn check_pat(&mut self, cx: &EarlyContext<'_>, pat: &Pat) { - if let PatKind::Ident(BindingMode::REF, _, _) = pat.kind +impl<'tcx> LateLintPass<'tcx> for RefPatterns { + fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'tcx>) { + if let PatKind::Binding(BindingMode::REF, _, _, _) = pat.kind && !pat.span.from_expansion() + && !in_automatically_derived(cx.tcx, pat.hir_id) { #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")] span_lint_and_then(cx, REF_PATTERNS, pat.span, "usage of ref pattern", |diag| { diff --git a/src/tools/clippy/clippy_lints/src/regex.rs b/src/tools/clippy/clippy_lints/src/regex.rs index 80696f0a81b15..837c29c08851b 100644 --- a/src/tools/clippy/clippy_lints/src/regex.rs +++ b/src/tools/clippy/clippy_lints/src/regex.rs @@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; use clippy_utils::paths; use clippy_utils::paths::PathLookup; use clippy_utils::res::MaybeQPath; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::ast::{LitKind, StrStyle}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId}; @@ -190,7 +190,7 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: ®ex_syntax::Error, unescape }; if let Some((primary, auxiliary, kind)) = parts - && let Some(literal_snippet) = base.get_source_text(cx) + && let Some(literal_snippet) = base.get_text(cx) && let Some(inner) = literal_snippet.get(offset as usize..) // Only convert to native rustc spans if the parsed regex matches the // source snippet exactly, to ensure the span offsets are correct diff --git a/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs b/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs index 2ec921ed21c7d..c1a909fdde4e1 100644 --- a/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs +++ b/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs @@ -67,7 +67,7 @@ pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) } } fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool { - for_each_expr(cx, expr, |e| { + for_each_expr(cx.tcx, expr, |e| { if let Some(def_id) = fn_def_id(cx, e) && cx .tcx diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs index e806123596b85..7fdc03891a889 100644 --- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs +++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs @@ -1,6 +1,6 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; @@ -94,7 +94,7 @@ impl SemicolonBlock { // ({ 0 }); // if we remove this `;`, this will parse as a `({ 0 })(5);` function call // (5); // } - if remove_span.check_source_text(cx, |src| src.contains(')')) { + if remove_span.check_text(cx, |src| src.contains(')')) { return; } diff --git a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs index 4aba49071ee56..03acfb22c6c82 100644 --- a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs +++ b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs @@ -127,7 +127,7 @@ fn find_insert_calls<'tcx>( contains_expr: &OpExpr<'tcx>, expr: &'tcx Expr<'_>, ) -> Option> { - for_each_expr(cx, expr, |e| { + for_each_expr(cx.tcx, expr, |e| { if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym::insert) && SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), contains_expr.receiver, insert_expr.receiver) && SpanlessEq::new(cx).eq_expr(SyntaxContext::root(), contains_expr.value, insert_expr.value) diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs index 4ddf82773d872..5072e39afb8c1 100644 --- a/src/tools/clippy/clippy_lints/src/shadow.rs +++ b/src/tools/clippy/clippy_lints/src/shadow.rs @@ -199,7 +199,7 @@ pub fn is_local_used_except<'tcx>( id: HirId, except: Option, ) -> bool { - for_each_expr(cx, visitable, |e| { + for_each_expr(cx.tcx, visitable, |e| { if except.is_some_and(|it| it == e.hir_id) { ControlFlow::Continue(Descend::No) } else if e.res_local_id() == Some(id) { diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs index aba51114c533a..67c8710b80ec8 100644 --- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs +++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs @@ -146,16 +146,24 @@ impl SingleComponentPathImports { // ``` let mut macros = Vec::new(); - let mut import_usage_visitor = ImportUsageVisitor::default(); for item in items { self.track_uses(item, &mut imports_reused_with_self, &mut single_use_usages, &mut macros); + } + + // Only walk the module's AST in search of `self::xxx` paths when there are single + // component imports left to lint, as the visitor recurses into every nested item. + single_use_usages.retain(|usage| !imports_reused_with_self.contains(&usage.name)); + if single_use_usages.is_empty() { + return; + } + + let mut import_usage_visitor = ImportUsageVisitor::default(); + for item in items { import_usage_visitor.visit_item(item); } for usage in single_use_usages { - if !imports_reused_with_self.contains(&usage.name) - && !import_usage_visitor.imports_referenced_with_self.contains(&usage.name) - { + if !import_usage_visitor.imports_referenced_with_self.contains(&usage.name) { self.found.entry(usage.item_id).or_default().push(usage); } } diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs index fc5702a358bbc..3d5de785b790c 100644 --- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs +++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs @@ -1,7 +1,7 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::higher::{Range, VecArgs}; use clippy_utils::macros::root_macro_call_first_node; -use clippy_utils::source::{SpanRangeExt, snippet_with_context}; +use clippy_utils::source::{SpanExt, snippet_with_context}; use clippy_utils::ty::implements_trait; use clippy_utils::{is_no_std_crate, sym}; use rustc_ast::{LitIntType, LitKind, RangeLimits, UintTy}; @@ -91,7 +91,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit { return; }; - let Some(snippet) = span.get_source_text(cx) else { + let Some(snippet) = span.get_text(cx) else { return; }; // `is_from_proc_macro` will skip any `vec![]`. Let's not! diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs index d519afba61b53..0f7cbd503bcd8 100644 --- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs +++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs @@ -238,20 +238,21 @@ fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> /// Does not catch individually moved items fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: Msrv) -> bool { loop { - if let Some(stability) = cx.tcx.lookup_stability(def_id) - && let StabilityLevel::Stable { - since, - allowed_through_unstable_modules: None, - } = stability.level - { - let stable = match since { - StableSince::Version(v) => msrv.meets(cx, v), - StableSince::Current => msrv.current(cx).is_none(), - StableSince::Err(_) => false, - }; - - if !stable { - return false; + if let Some(stability) = cx.tcx.lookup_stability(def_id) { + match stability.level { + // Workaround for items from `core::intrinsics` with a stable export in a different module. + // Not that we ignore the `since` field as we are already accessing the item in question. + StabilityLevel::Stable { + allowed_through_unstable_modules: Some(_), + .. + } => return true, + StabilityLevel::Stable { since, .. } => match since { + StableSince::Version(v) if !msrv.meets(cx, v) => return false, + StableSince::Current if msrv.current(cx).is_none() => return false, + StableSince::Err(_) => return false, + StableSince::Version(_) | StableSince::Current => {}, + }, + StabilityLevel::Unstable { .. } => return false, } } diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs index b4eb8977bf0fb..986429ec0829e 100644 --- a/src/tools/clippy/clippy_lints/src/string_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs @@ -147,7 +147,7 @@ fn check_manual_pattern_char_comparison(cx: &LateContext<'_>, method_arg: &Expr< // We want to retrieve all the comparisons done. // They are ordered in a nested way and so we need to traverse the AST to collect them all. - if for_each_expr(cx, body.value, |sub_expr| -> ControlFlow<(), Descend> { + if for_each_expr(cx.tcx, body.value, |sub_expr| -> ControlFlow<(), Descend> { match sub_expr.kind { ExprKind::Binary(op, left, right) if op.node == BinOpKind::Eq => { if left.res_local_id() == Some(binding) @@ -230,13 +230,13 @@ impl<'tcx> LateLintPass<'tcx> for StringPatterns { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { if !expr.span.from_expansion() && let ExprKind::MethodCall(method, receiver, args, _) = expr.kind - && let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() - && ty.is_str() && let method_name = method.ident.name && let Some(&(_, pos)) = PATTERN_METHODS .iter() .find(|(array_method_name, _)| *array_method_name == method_name) && let Some(arg) = args.get(pos) + && let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(receiver).kind() + && ty.is_str() { check_single_char_pattern_lint(cx, arg); diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs index 7b690b7eb136c..16cf0ecbc0bc7 100644 --- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs +++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg}; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability}; +use clippy_utils::source::{SpanExt, snippet, snippet_with_applicability}; use clippy_utils::{SpanlessEq, SpanlessHash, is_from_proc_macro}; use core::hash::{Hash, Hasher}; use itertools::Itertools; @@ -212,10 +212,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds { bounds_span = bounds_span.to(bound.span); } - let fixed_trait_snippet = unique_traits - .iter() - .filter_map(|b| b.span.get_source_text(cx)) - .join(" + "); + let fixed_trait_snippet = unique_traits.iter().filter_map(|b| b.span.get_text(cx)).join(" + "); span_lint_and_sugg( cx, @@ -451,7 +448,7 @@ fn rollup_traits<'cx, 'tcx>( let traits = comparable_bounds .iter() - .filter_map(|&(_, span)| span.get_source_text(cx)) + .filter_map(|&(_, span)| span.get_text(cx)) .join(" + "); span_lint_and_sugg( diff --git a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs index 42f3e06b7d6f5..d121c5385b3c7 100644 --- a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs +++ b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs @@ -1,7 +1,7 @@ use std::borrow::Cow; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{HasSession, SpanRangeExt as _}; +use clippy_utils::source::SpanExt as _; use rustc_errors::Applicability; use rustc_hir::{Expr, GenericArg, HirId, LetStmt, Node, Path, TyKind}; use rustc_lint::LateContext; @@ -114,8 +114,8 @@ fn ty_cannot_be_named(ty: Ty<'_>) -> bool { ) } -fn maybe_name_by_expr<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> { - span.with_source_text(sess, |name| { +fn maybe_name_by_expr<'a>(cx: &LateContext<'_>, span: Span, default: &'a str) -> Cow<'a, str> { + span.with_source_text(cx, |name| { (name.len() + 9 < default.len()).then_some(format!("`{name}`'s type").into()) }) .flatten() diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs index 2bf1d8be46536..d35139f2f6b9b 100644 --- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs +++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs @@ -393,7 +393,7 @@ fn expr_has_unnecessary_safety_comment<'tcx>( } // this should roughly be the reverse of `block_parents_have_safety_comment` - if for_each_expr(cx, expr, |expr| match expr.kind { + if for_each_expr(cx.tcx, expr, |expr| match expr.kind { hir::ExprKind::Block( Block { rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided), diff --git a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs index 735225a9e2cee..13baa378910d9 100644 --- a/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs +++ b/src/tools/clippy/clippy_lints/src/unit_return_expecting_ord.rs @@ -1,4 +1,4 @@ -use clippy_utils::diagnostics::{span_lint, span_lint_and_help}; +use clippy_utils::diagnostics::span_lint_and_then; use rustc_hir::def_id::DefId; use rustc_hir::{Closure, Expr, ExprKind, StmtKind}; use rustc_lint::{LateContext, LateLintPass}; @@ -180,32 +180,21 @@ impl<'tcx> LateLintPass<'tcx> for UnitReturnExpectingOrd { let args = std::iter::once(receiver).chain(args.iter()).collect::>(); let arg_indices = get_args_to_check(cx, expr, args.len(), fn_mut_trait, ord_trait, partial_ord_trait); for (i, trait_name) in arg_indices { - match check_arg(cx, args[i]) { - Some((span, None)) => { - span_lint( - cx, - UNIT_RETURN_EXPECTING_ORD, - span, - format!( - "this closure returns \ + if let Some((span, last_semi)) = check_arg(cx, args[i]) { + span_lint_and_then( + cx, + UNIT_RETURN_EXPECTING_ORD, + span, + format!( + "this closure returns \ the unit type which also implements {trait_name}" - ), - ); - }, - Some((span, Some(last_semi))) => { - span_lint_and_help( - cx, - UNIT_RETURN_EXPECTING_ORD, - span, - format!( - "this closure returns \ - the unit type which also implements {trait_name}" - ), - Some(last_semi), - "probably caused by this trailing semicolon", - ); - }, - None => {}, + ), + |diag| { + if let Some(last_semi) = last_semi { + diag.span_help(last_semi, "probably caused by this trailing semicolon"); + } + }, + ); } } } diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs index 6df3e7baa1bca..b9d6c81a152b3 100644 --- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs +++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs @@ -1,7 +1,7 @@ use std::iter; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::{SpanRangeExt, indent_of, reindent_multiline}; +use clippy_utils::source::{SpanExt, indent_of, reindent_multiline}; use clippy_utils::sugg::Sugg; use clippy_utils::ty::expr_type_is_certain; use clippy_utils::{is_empty_block, is_expr_default, is_from_proc_macro}; @@ -85,7 +85,7 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_ && block.expr.is_none() && let Some(last_stmt) = block.stmts.iter().last() && let StmtKind::Semi(last_expr) = last_stmt.kind - && let Some(snip) = last_expr.span.get_source_text(cx) + && let Some(snip) = last_expr.span.get_text(cx) { Some((last_stmt.span, snip)) } else { @@ -117,7 +117,7 @@ fn lint_unit_args<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, args_to_ .filter_map(|arg| get_expr_snippet_with_type_certainty(cx, arg)) .collect(); - if let Some(call_snippet) = expr.span.get_source_text(cx) { + if let Some(call_snippet) = expr.span.get_text(cx) { if arg_snippets_without_redundant_exprs.is_empty() && let suggestions = args_to_recover .iter() diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs index 8d4f1a153def8..4112e1b6ca63f 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs @@ -1,10 +1,10 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::ty::approx_ty_size; use rustc_errors::Applicability; use rustc_hir::def_id::LocalDefId; use rustc_hir::{FnDecl, FnRetTy, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind}; use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::ty::layout::LayoutOf; use rustc_session::impl_lint_pass; use rustc_span::Symbol; @@ -81,8 +81,13 @@ impl UnnecessaryBoxReturns { // It's sometimes useful to return Box if T is unsized, so don't lint those. // Also, don't lint if we know that T is very large, in which case returning - // a Box may be beneficial. - if boxed_ty.is_sized(cx.tcx, cx.typing_env()) && approx_ty_size(cx, boxed_ty) <= self.maximum_size { + // a Box may be beneficial. When the size depends on generic parameters + // (e.g. `[T; N]`) it cannot be determined here, so don't lint that either, as + // the `Box` may be a deliberate choice to avoid copying a large value. + if boxed_ty.is_sized(cx.tcx, cx.typing_env()) + && let Ok(layout) = cx.layout_of(boxed_ty) + && layout.size.bytes() <= self.maximum_size + { span_lint_and_then( cx, UNNECESSARY_BOX_RETURNS, diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs b/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs index 75b0b7b10687a..60a6688927ab5 100644 --- a/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs +++ b/src/tools/clippy/clippy_lints/src/unnecessary_mut_passed.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_errors::Applicability; use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs index bfbfd0903bbe8..0a6f3f5992ece 100644 --- a/src/tools/clippy/clippy_lints/src/unused_async.rs +++ b/src/tools/clippy/clippy_lints/src/unused_async.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::is_def_id_trait_method; -use clippy_utils::source::{HasSession, snippet_with_applicability, walk_span_to_context}; +use clippy_utils::source::{snippet_with_applicability, walk_span_to_context}; use clippy_utils::usage::is_todo_unimplemented_stub; use rustc_errors::Applicability; use rustc_hir::def::DefKind; @@ -303,7 +303,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync { // evaluate the expression, to immediately evaluate the expression. let mut app = Applicability::MaybeIncorrect; - let async_span = cx.sess().source_map().span_extend_while_whitespace(async_span); + let async_span = cx.tcx.sess.source_map().span_extend_while_whitespace(async_span); let signature_snippet = snippet_with_applicability(cx, signature_span, "_", &mut app); let tail_snippet = snippet_with_applicability(cx, tail_span, "_", &mut app).to_string(); diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs index d503bd3379b08..bb2ad9f92157c 100644 --- a/src/tools/clippy/clippy_lints/src/unused_unit.rs +++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_sugg; -use clippy_utils::source::{SpanRangeExt, position_before_rarrow}; +use clippy_utils::source::{SpanExt, position_before_rarrow}; use clippy_utils::{is_never_expr, is_unit_expr}; use rustc_ast::{Block, StmtKind}; use rustc_errors::Applicability; diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs index d6db088ba76c0..766659f426aea 100644 --- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs +++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs @@ -132,8 +132,8 @@ fn into_iter_bound<'tcx>( /// Extracts the receiver of a `.into_iter()` method call. fn into_iter_call<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Option<&'hir Expr<'hir>> { if let ExprKind::MethodCall(name, recv, [], _) = expr.kind - && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IntoIterator) && name.ident.name == sym::into_iter + && cx.ty_based_def(expr).opt_parent(cx).is_diag_item(cx, sym::IntoIterator) { Some(recv) } else { @@ -208,7 +208,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { }, ExprKind::MethodCall(name, recv, [], _) => { - if cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Into) && name.ident.name == sym::into { + if name.ident.name == sym::into && cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::Into) { let a = cx.typeck_results().expr_ty(e); let b = cx.typeck_results().expr_ty(recv); if same_type_modulo_regions(a, b) { @@ -393,8 +393,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion { ); } } - if cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::TryInto) - && name.ident.name == sym::try_into + if name.ident.name == sym::try_into + && cx.ty_based_def(e).opt_parent(cx).is_diag_item(cx, sym::TryInto) && let a = cx.typeck_results().expr_ty(e) && let b = cx.typeck_results().expr_ty(recv) && a.is_diag_item(cx, sym::Result) diff --git a/src/tools/clippy/clippy_lints/src/useless_vec.rs b/src/tools/clippy/clippy_lints/src/useless_vec.rs index 7b6122213d608..cdb757ed89e23 100644 --- a/src/tools/clippy/clippy_lints/src/useless_vec.rs +++ b/src/tools/clippy/clippy_lints/src/useless_vec.rs @@ -7,7 +7,7 @@ use clippy_config::Conf; use clippy_utils::consts::{ConstEvalCtxt, Constant}; use clippy_utils::diagnostics::span_lint_hir_and_then; use clippy_utils::msrvs::{self, Msrv}; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::ty::is_copy; use clippy_utils::visitors::for_each_local_use_after_expr; use clippy_utils::{VEC_METHODS_SHADOWING_SLICE_METHODS, get_parent_expr, higher, is_in_test, span_contains_comment}; @@ -285,10 +285,10 @@ impl SuggestedType { assert!(args_span.is_none_or(|s| !s.from_expansion())); assert!(len_span.is_none_or(|s| !s.from_expansion())); - let maybe_args = args_span.map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")); + let maybe_args = args_span.map(|sp| sp.get_text(cx).expect("spans are always crate-local")); let maybe_args = maybe_args.as_deref().unwrap_or_default(); let maybe_len = len_span - .map(|sp| sp.get_source_text(cx).expect("spans are always crate-local")) + .map(|sp| sp.get_text(cx).expect("spans are always crate-local")) .map(|st| format!("; {st}")) .unwrap_or_default(); diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index c60d84785a229..2bcf369fb3334 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -6,11 +6,11 @@ use rustc_ast::ast::{LitFloatType, LitKind}; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def_id::DefId; use rustc_hir::{ - self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind, + self as hir, BindingMode, Body, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind, FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, }; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::{FloatTy, IntTy, UintTy}; +use rustc_middle::ty::{FloatTy, IntTy, TypeckResults, UintTy}; use rustc_session::declare_lint_pass; use rustc_span::symbol::{Ident, Symbol}; use std::cell::Cell; @@ -137,15 +137,31 @@ impl<'tcx> LateLintPass<'tcx> for Author { fn check_item(cx: &LateContext<'_>, hir_id: HirId) { if let Some(body) = cx.tcx.hir_maybe_body_owned_by(hir_id.expect_owner().def_id) { - check_node(cx, hir_id, |v| { - v.expr(&v.bind("expr", body.value)); - }); + check_node_with_body( + cx, + hir_id, + |v| { + v.expr(&v.bind("expr", body.value)); + }, + Some(body), + ); } } fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) { + check_node_with_body(cx, hir_id, f, None); +} + +/// Check the node at `hir_id`, in the context of `body` or the default from `cx` if none is given. +fn check_node_with_body( + cx: &LateContext<'_>, + hir_id: HirId, + f: impl Fn(&PrintVisitor<'_, '_>), + body: Option<&Body<'_>>, +) { if has_attr(cx, hir_id) { - f(&PrintVisitor::new(cx)); + let typeck_results = body.map_or_else(|| cx.typeck_results(), |body| cx.tcx.typeck_body(body.id())); + f(&PrintVisitor::new(cx, typeck_results)); println!("{{"); println!(" // report your lint here"); println!("}}"); @@ -199,6 +215,7 @@ impl Display for OptionPat { struct PrintVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, + typeck_results: &'tcx TypeckResults<'tcx>, /// Fields are the current index that needs to be appended to pattern /// binding names ids: Cell>, @@ -207,9 +224,10 @@ struct PrintVisitor<'a, 'tcx> { } impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>) -> Self { + fn new(cx: &'a LateContext<'tcx>, typeck_results: &'tcx TypeckResults<'tcx>) -> Self { Self { cx, + typeck_results, ids: Cell::default(), first: Cell::new(true), } @@ -291,7 +309,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { } fn maybe_path<'p>(&self, path: &Binding>) { - if let Some(id) = path.value.res(self.cx).opt_def_id() + if let Some(id) = path.value.res(self.typeck_results).opt_def_id() && !id.is_local() { if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) { diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs index 6629a67f78bd4..6401b48f70b05 100644 --- a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs +++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs @@ -1,18 +1,18 @@ use clippy_utils::macros::FormatArgsStorage; -use clippy_utils::source::SpanRangeExt; -use itertools::Itertools; +use clippy_utils::source::{SpanExt, walk_span_to_context}; use rustc_ast::{Crate, Expr, ExprKind, FormatArgs}; use rustc_data_structures::fx::FxHashMap; use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; -use rustc_lint::{EarlyContext, EarlyLintPass}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::impl_lint_pass; -use rustc_span::{Span, hygiene}; -use std::iter::once; +use rustc_span::source_map::SourceMap; +use rustc_span::{Span, SpanData}; use std::mem; /// Populates [`FormatArgsStorage`] with AST [`FormatArgs`] nodes pub struct FormatArgsCollector { format_args: FxHashMap, + parent_spans: Vec, storage: FormatArgsStorage, } @@ -20,6 +20,7 @@ impl FormatArgsCollector { pub fn new(storage: FormatArgsStorage) -> Self { Self { format_args: FxHashMap::default(), + parent_spans: Vec::new(), storage, } } @@ -30,7 +31,7 @@ impl_lint_pass!(FormatArgsCollector => []); impl EarlyLintPass for FormatArgsCollector { fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) { if let ExprKind::FormatArgs(args) = &expr.kind { - if has_span_from_proc_macro(cx, args) { + if self.has_span_from_external_macro(cx.sess().source_map(), expr.span, args) { return; } @@ -43,53 +44,99 @@ impl EarlyLintPass for FormatArgsCollector { } } -/// Detects if the format string or an argument has its span set by a proc macro to something inside -/// a macro callsite, e.g. -/// -/// ```ignore -/// println!(some_proc_macro!("input {}"), a); -/// ``` -/// -/// Where `some_proc_macro` expands to -/// -/// ```ignore -/// println!("output {}", a); -/// ``` -/// -/// But with the span of `"output {}"` set to the macro input -/// -/// ```ignore -/// println!(some_proc_macro!("input {}"), a); -/// // ^^^^^^^^^^ -/// ``` -fn has_span_from_proc_macro(cx: &EarlyContext<'_>, args: &FormatArgs) -> bool { - let ctxt = args.span.ctxt(); +impl FormatArgsCollector { + /// Detects if the format string or an argument has its span set by a proc macro to something + /// inside a macro callsite, e.g. + /// + /// ```ignore + /// println!(some_proc_macro!("input {}"), a); + /// ``` + /// + /// Where `some_proc_macro` expands to + /// + /// ```ignore + /// println!("output {}", a); + /// ``` + /// + /// But with the span of `"output {}"` set to the macro input + /// + /// ```ignore + /// println!(some_proc_macro!("input {}"), a); + /// // ^^^^^^^^^^ + /// ``` + fn has_span_from_external_macro(&mut self, sm: &SourceMap, fmt_sp: Span, args: &FormatArgs) -> bool { + let mut fmt_sp = fmt_sp.data(); + + // Find the first macro call that contains the format string. + let arg_sp = if let Some(arg_sp) = walk_span_to_context(args.span, fmt_sp.ctxt) { + arg_sp.data() + } else { + // Try to find a common parent for the format call and the format string. + self.parent_spans.clear(); + // `fmt_sp.ctxt` isn't a parent of the format string so don't add it to the + // search. The first iteration will always run since it can't be the root. + while !fmt_sp.ctxt.is_root() { + fmt_sp = fmt_sp.ctxt.outer_expn_data().call_site.data(); + self.parent_spans.push(fmt_sp); + } + let mut arg_sp = args.span.data(); + // Note: A parent span will always eventually be found since the root context + // is an ancestor of all contexts. + loop { + match self.parent_spans.iter().find(|s| s.ctxt == arg_sp.ctxt) { + Some(call_sp) if call_sp.lo <= arg_sp.lo && arg_sp.hi <= call_sp.hi => { + fmt_sp = *call_sp; + break arg_sp; + }, + // If the string isn't within the call span we some macro stuff we can't + // easily interpret. + Some(_) => return true, + None => arg_sp = arg_sp.ctxt.outer_expn_data().call_site.data(), + } + } + }; + if fmt_sp.ctxt.in_external_macro(sm) { + return true; + } + let Some(src) = arg_sp.get_source_range(sm) else { + return true; + }; + let Some(src_text) = src.sf.src.as_ref().map(|x| &***x) else { + return true; + }; - // `format!("{} {} {c}", "one", "two", c = "three")` - // ^^^^^ ^^^^^ ^^^^^^^ - let argument_span = args - .arguments - .explicit_args() - .iter() - .map(|argument| hygiene::walk_chain(argument.expr.span, ctxt)); + // Check the spans between the format string and the arguments and between each argument. + args.arguments + .explicit_args() + .iter() + .try_fold(src.range.end, |start, arg| { + let expr_sp = walk_span_to_context(arg.expr.span, fmt_sp.ctxt)?.data(); + let expr_start = (expr_sp.lo.0 - src.sf.start_pos.0) as usize; + let expr_end = (expr_sp.hi.0 - src.sf.start_pos.0) as usize; + let mut tks = tokenize(src_text.get(start..expr_start)?, FrontmatterAllowed::No) + .map(|x| x.kind) + .filter(|x| { + !matches!( + x, + TokenKind::LineComment { doc_style: None } + | TokenKind::BlockComment { + doc_style: None, + terminated: true + } + | TokenKind::Whitespace + ) + }); - // `format!("{} {} {c}", "one", "two", c = "three")` - // ^^ ^^ ^^^^^^ - !once(args.span) - .chain(argument_span) - .tuple_windows() - .map(|(start, end)| start.between(end)) - .all(|sp| { - sp.check_source_text(cx, |src| { - // text should be either `, name` or `, name =` - let mut iter = tokenize(src, FrontmatterAllowed::No).filter(|t| { - !matches!( - t.kind, - TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace - ) - }); - iter.next().is_some_and(|t| matches!(t.kind, TokenKind::Comma)) - && iter.all(|t| matches!(t.kind, TokenKind::Ident | TokenKind::Eq)) + // `,` or `, ident =` + let matches = matches!(tks.next(), Some(TokenKind::Comma)) + && match tks.next() { + Some(TokenKind::Ident) => matches!(tks.next(), Some(TokenKind::Eq)), + Some(_) => false, + None => true, + } + && tks.next().is_none(); + matches.then_some(expr_end) }) - }) + .is_none() + } } diff --git a/src/tools/clippy/clippy_lints/src/visibility.rs b/src/tools/clippy/clippy_lints/src/visibility.rs index 74ffeb629d2be..9cefc54598b78 100644 --- a/src/tools/clippy/clippy_lints/src/visibility.rs +++ b/src/tools/clippy/clippy_lints/src/visibility.rs @@ -1,5 +1,5 @@ use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use rustc_ast::ast::{Item, VisibilityKind}; use rustc_errors::Applicability; use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; @@ -152,5 +152,5 @@ impl EarlyLintPass for Visibility { } fn is_from_proc_macro(cx: &EarlyContext<'_>, span: Span) -> bool { - !span.check_source_text(cx, |src| src.starts_with("pub")) + !span.check_text(cx, |src| src.starts_with("pub")) } diff --git a/src/tools/clippy/clippy_lints/src/write/literal.rs b/src/tools/clippy/clippy_lints/src/write/literal.rs index 699ac7ea7a5cd..df31fad8615ed 100644 --- a/src/tools/clippy/clippy_lints/src/write/literal.rs +++ b/src/tools/clippy/clippy_lints/src/write/literal.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::format_arg_removal_span; -use clippy_utils::source::SpanRangeExt; +use clippy_utils::source::SpanExt; use clippy_utils::sym; use rustc_ast::token::LitKind; use rustc_ast::{ @@ -48,7 +48,7 @@ pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) && let Some(arg) = format_args.arguments.by_index(index) && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind && !arg.expr.span.from_expansion() - && let Some(value_string) = arg.expr.span.get_source_text(cx) + && let Some(value_string) = arg.expr.span.get_text(cx) { let (replacement, replace_raw) = match lit.kind { LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) { @@ -71,7 +71,7 @@ pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) _ => continue, }; - let Some(format_string_snippet) = format_args.span.get_source_text(cx) else { + let Some(format_string_snippet) = format_args.span.get_text(cx) else { continue; }; let format_string_is_raw = format_string_snippet.starts_with('r'); diff --git a/src/tools/clippy/clippy_lints/src/write/with_newline.rs b/src/tools/clippy/clippy_lints/src/write/with_newline.rs index e4b51da3cadcf..15cc29818aec4 100644 --- a/src/tools/clippy/clippy_lints/src/write/with_newline.rs +++ b/src/tools/clippy/clippy_lints/src/write/with_newline.rs @@ -1,6 +1,6 @@ use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::macros::MacroCall; -use clippy_utils::source::{SpanRangeExt, expand_past_previous_comma}; +use clippy_utils::source::{SpanExt, expand_past_previous_comma}; use clippy_utils::sym; use rustc_ast::{FormatArgs, FormatArgsPiece}; use rustc_errors::Applicability; @@ -48,7 +48,7 @@ pub(super) fn check(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: format!("using `{name}!()` with a format string that ends in a single newline"), |diag| { let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!'); - let Some(format_snippet) = format_string_span.get_source_text(cx) else { + let Some(format_snippet) = format_string_span.get_text(cx) else { return; }; diff --git a/src/tools/clippy/clippy_lints_internal/src/lib.rs b/src/tools/clippy/clippy_lints_internal/src/lib.rs index 8e2166858fec8..f75a9f67d0563 100644 --- a/src/tools/clippy/clippy_lints_internal/src/lib.rs +++ b/src/tools/clippy/clippy_lints_internal/src/lib.rs @@ -63,15 +63,23 @@ static LINTS: &[&Lint] = &[ pub fn register_lints(store: &mut LintStore) { store.register_lints(LINTS); - store.register_early_pass(|| Box::new(unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths)); - store.register_early_pass(|| Box::new(produce_ice::ProduceIce)); - store.register_late_pass(|_| Box::new(collapsible_span_lint_calls::CollapsibleCalls)); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::::default()); - store.register_late_pass(|_| Box::new(unnecessary_def_path::UnnecessaryDefPath)); - store.register_late_pass(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass)); - store.register_late_pass(|_| Box::new(msrv_attr_impl::MsrvAttrImpl)); - store.register_late_pass(|_| Box::new(almost_standard_lint_formulation::AlmostStandardFormulation::new())); - store.register_late_pass(|_| Box::new(unusual_names::UnusualNames)); - store.register_late_pass(|_| Box::new(repeated_is_diagnostic_item::RepeatedIsDiagnosticItem)); + store.register_early_pass(Box::new(|| { + Box::new(unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths) + })); + store.register_early_pass(Box::new(|| Box::new(produce_ice::ProduceIce))); + store.register_late_pass(Box::new(|_| Box::new(collapsible_span_lint_calls::CollapsibleCalls))); + store.register_late_pass(Box::new(|_| Box::::default())); + store.register_late_pass(Box::new(|_| { + Box::::default() + })); + store.register_late_pass(Box::new(|_| Box::new(unnecessary_def_path::UnnecessaryDefPath))); + store.register_late_pass(Box::new(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass))); + store.register_late_pass(Box::new(|_| Box::new(msrv_attr_impl::MsrvAttrImpl))); + store.register_late_pass(Box::new(|_| { + Box::new(almost_standard_lint_formulation::AlmostStandardFormulation::new()) + })); + store.register_late_pass(Box::new(|_| Box::new(unusual_names::UnusualNames))); + store.register_late_pass(Box::new(|_| { + Box::new(repeated_is_diagnostic_item::RepeatedIsDiagnosticItem) + })); } diff --git a/src/tools/clippy/clippy_lints_internal/src/repeated_is_diagnostic_item.rs b/src/tools/clippy/clippy_lints_internal/src/repeated_is_diagnostic_item.rs index b300dfa27b0eb..e0148c79477c0 100644 --- a/src/tools/clippy/clippy_lints_internal/src/repeated_is_diagnostic_item.rs +++ b/src/tools/clippy/clippy_lints_internal/src/repeated_is_diagnostic_item.rs @@ -541,7 +541,7 @@ fn extract_nested_is_diag_item<'tcx>( cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, ) -> Option<(Span, (&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>))> { - for_each_expr(cx, cond, |cond_part| { + for_each_expr(cx.tcx, cond, |cond_part| { if let Some(res) = extract_is_diag_item(cx, cond_part) { ControlFlow::Break((cond_part.span, res)) } else { @@ -554,7 +554,7 @@ fn extract_nested_is_diagnostic_item<'tcx>( cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, ) -> Option<(Span, (&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>))> { - for_each_expr(cx, cond, |cond_part| { + for_each_expr(cx.tcx, cond, |cond_part| { if let Some(res) = extract_is_diagnostic_item(cx, cond_part) { ControlFlow::Break((cond_part.span, res)) } else { diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md index 95b8bdde5cd91..f322d03f18795 100644 --- a/src/tools/clippy/clippy_utils/README.md +++ b/src/tools/clippy/clippy_utils/README.md @@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain: ``` -nightly-2026-06-11 +nightly-2026-06-25 ``` diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs index 8df82489d6a3f..7718afc5f0222 100644 --- a/src/tools/clippy/clippy_utils/src/attrs.rs +++ b/src/tools/clippy/clippy_utils/src/attrs.rs @@ -1,6 +1,6 @@ //! Utility functions for attributes, including Clippy's built-in ones -use crate::source::SpanRangeExt; +use crate::source::SpanExt; use crate::{sym, tokenize_with_text}; use rustc_ast::attr::AttributeExt; use rustc_errors::Applicability; @@ -37,8 +37,6 @@ pub fn check_clippy_attr(sess: &Session, attr: &A) { | sym::version | sym::cognitive_complexity | sym::dump - | sym::disallowed_profile - | sym::disallowed_profiles | sym::msrv | sym::has_significant_drop | sym::format_args => {}, @@ -107,7 +105,12 @@ pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool { /// Checks whether the given span contains a `#[cfg(..)]` attribute pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool { - s.check_source_text(cx, |src| { + s.check_text(cx, |src| { + // PERF: A `#[cfg]` needs a literal `#`, so skip the lexer when the source has none. + if !src.contains('#') { + return false; + } + let mut iter = tokenize_with_text(src); // Search for the token sequence [`#`, `[`, `cfg`] diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs index 098be98f3672c..11e6e32ba6c56 100644 --- a/src/tools/clippy/clippy_utils/src/consts.rs +++ b/src/tools/clippy/clippy_utils/src/consts.rs @@ -5,7 +5,7 @@ #![expect(clippy::float_cmp)] use crate::res::MaybeDef; -use crate::source::{SpanRangeExt, walk_span_to_context}; +use crate::source::{SpanExt, walk_span_to_context}; use crate::{clip, is_direct_expn_of, sext, sym, unsext}; use rustc_abi::Size; @@ -522,9 +522,9 @@ pub fn eval_int(cx: &LateContext<'_>, e: &Expr<'_>) -> Option { /// /// See the module level documentation for some context. pub struct ConstEvalCtxt<'tcx> { - tcx: TyCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - typeck: &'tcx TypeckResults<'tcx>, + pub tcx: TyCtxt<'tcx>, + pub typing_env: ty::TypingEnv<'tcx>, + pub typeck: &'tcx TypeckResults<'tcx>, source: Cell, ctxt: Cell, } @@ -885,12 +885,22 @@ impl<'tcx> ConstEvalCtxt<'tcx> { _ => return None, }; + let args = self.typeck.node_args(id); + + if !args.is_empty() { + let owner_def_id = self.typeck.hir_owner.def_id.to_def_id(); + let identity_args = ty::GenericArgs::identity_for_item(self.tcx, owner_def_id); + // Don't try to fully evaluate consts inside code whose bounds can't be satisfied. + if self + .tcx + .instantiate_and_check_impossible_predicates((owner_def_id, identity_args)) + { + return None; + } + } + self.tcx - .const_eval_resolve( - self.typing_env, - mir::UnevaluatedConst::new(did, self.typeck.node_args(id)), - qpath.span(), - ) + .const_eval_resolve(self.typing_env, mir::UnevaluatedConst::new(did, args), qpath.span()) .ok() } @@ -934,7 +944,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> { if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt) && let expr_lo = expr_span.lo() && expr_lo >= span.lo - && let Some(src) = (span.lo..expr_lo).get_source_range(&self.tcx) + && let Some(src) = (span.lo..expr_lo).get_source_range(self.tcx) && let Some(src) = src.as_str() { use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace}; diff --git a/src/tools/clippy/clippy_utils/src/disallowed_profiles.rs b/src/tools/clippy/clippy_utils/src/disallowed_profiles.rs deleted file mode 100644 index 98a498981b3b5..0000000000000 --- a/src/tools/clippy/clippy_utils/src/disallowed_profiles.rs +++ /dev/null @@ -1,180 +0,0 @@ -use crate::sym; -use rustc_ast::ast::{LitKind, MetaItemInner}; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::smallvec::SmallVec; -use rustc_hir::{Attribute, HirId}; -use rustc_lint::LateContext; -use rustc_span::{Span, Symbol}; - -/// One profile name referenced by a `#[clippy::disallowed_profile(...)]` or -/// `#[clippy::disallowed_profiles(...)]` attribute on an item. -/// -/// A single attribute produces one `ProfileEntry` per string argument. The entry records which -/// attribute variant introduced it, the profile name, and the span of that string literal so -/// diagnostics (e.g. "unknown profile") can point at the exact argument. -#[derive(Copy, Clone)] -pub struct ProfileEntry { - pub attr_name: Symbol, - pub name: Symbol, - pub span: Span, -} - -/// The set of profiles active at some `HirId`, obtained by walking up the HIR from that id and -/// collecting the first ancestor that carries a `#[clippy::disallowed_profile(s)]` attribute. -/// -/// An empty selection is represented by `None` at the call site; a `ProfileSelection` is always -/// non-empty. -#[derive(Clone)] -pub struct ProfileSelection { - entries: SmallVec<[ProfileEntry; 2]>, -} - -impl ProfileSelection { - pub fn new(entries: SmallVec<[ProfileEntry; 2]>) -> Self { - Self { entries } - } - - pub fn is_empty(&self) -> bool { - self.entries.is_empty() - } - - pub fn iter(&self) -> impl Iterator { - self.entries.iter() - } -} - -#[derive(Default)] -pub struct ProfileResolver { - cache: FxHashMap>, -} - -impl ProfileResolver { - pub fn active_profiles(&mut self, cx: &LateContext<'_>, hir_id: HirId) -> Option<&ProfileSelection> { - // NOTE: The `contains_key`+`get` dance is intentional: using only `get` here triggers borrowck - // errors because we need to mutate `self.cache` on cache misses. - if self.cache.contains_key(&hir_id) { - return self.cache.get(&hir_id).and_then(|selection| selection.as_ref()); - } - - let (resolved, visited) = self.resolve(cx, hir_id); - - for id in visited { - self.cache.entry(id).or_insert_with(|| resolved.clone()); - } - self.cache.insert(hir_id, resolved); - - self.cache.get(&hir_id).and_then(|selection| selection.as_ref()) - } - - fn resolve(&self, cx: &LateContext<'_>, start: HirId) -> (Option, SmallVec<[HirId; 8]>) { - let mut visited = SmallVec::<[HirId; 8]>::new(); - let mut current = Some(start); - - while let Some(id) = current { - if let Some(cached) = self.cache.get(&id) { - return (cached.clone(), visited); - } - - visited.push(id); - - if let Some(selection) = profiles_from_attrs(cx, cx.tcx.hir_attrs(id)) { - return (Some(selection), visited); - } - - if id == rustc_hir::CRATE_HIR_ID { - current = None; - } else { - current = Some(cx.tcx.parent_hir_id(id)); - } - } - - (None, visited) - } -} - -fn profiles_from_attrs(cx: &LateContext<'_>, attrs: &[Attribute]) -> Option { - let mut entries = SmallVec::<[ProfileEntry; 2]>::new(); - - for attr in attrs { - let path = attr.path(); - if path.len() != 2 || path[0] != sym::clippy { - continue; - } - - let name = path[1]; - if name != sym::disallowed_profile && name != sym::disallowed_profiles { - continue; - } - - let attr_label = if name == sym::disallowed_profiles { - "`clippy::disallowed_profiles`" - } else { - "`clippy::disallowed_profile`" - }; - - let Some(items) = attr.meta_item_list() else { - cx.tcx - .sess - .dcx() - .struct_span_err(attr.span(), format!("{attr_label} expects string arguments")) - .emit(); - continue; - }; - - if items.is_empty() { - cx.tcx - .sess - .dcx() - .struct_span_err(attr.span(), format!("{attr_label} expects at least one profile name")) - .emit(); - continue; - } - - if name == sym::disallowed_profile && items.len() != 1 { - cx.tcx - .sess - .dcx() - .struct_span_err(attr.span(), "use `clippy::disallowed_profiles` for multiple profiles") - .emit(); - } - - for item in items { - match literal_symbol(&item) { - Some((symbol, span)) => entries.push(ProfileEntry { - attr_name: name, - name: symbol, - span, - }), - None => emit_string_error(cx, &item), - } - } - } - - if entries.is_empty() { - None - } else { - Some(ProfileSelection::new(entries)) - } -} - -fn literal_symbol(item: &MetaItemInner) -> Option<(Symbol, Span)> { - match item { - MetaItemInner::Lit(lit) => { - let LitKind::Str(symbol, _) = lit.kind else { return None }; - Some((symbol, lit.span)) - }, - MetaItemInner::MetaItem(_) => None, - } -} - -fn emit_string_error(cx: &LateContext<'_>, item: &MetaItemInner) { - let span = match item { - MetaItemInner::Lit(lit) => lit.span, - MetaItemInner::MetaItem(meta) => meta.span, - }; - cx.tcx - .sess - .dcx() - .struct_span_err(span, "expected string literal profile name") - .emit(); -} diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index cd609980b149b..1c133fccdb308 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -11,15 +11,15 @@ use crate::consts::{ConstEvalCtxt, FullInt}; use crate::sym; -use crate::ty::{all_predicates_of, is_copy}; +use crate::ty::all_predicates_of; use crate::visitors::is_const_evaluatable; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, QPath, UnOp}; use rustc_lint::LateContext; -use rustc_middle::ty; use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_span::Symbol; use std::{cmp, ops}; @@ -48,17 +48,17 @@ impl ops::BitOrAssign for EagernessSuggestion { } /// Determine the eagerness of the given function call. -fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion { +fn fn_eagerness(tcx: TyCtxt<'_>, fn_id: DefId, name: Symbol, have_one_arg: bool) -> EagernessSuggestion { use EagernessSuggestion::{Eager, Lazy, NoChange}; - let ty = match cx.tcx.impl_of_assoc(fn_id) { - Some(id) => cx.tcx.type_of(id).instantiate_identity().skip_norm_wip(), + let ty = match tcx.impl_of_assoc(fn_id) { + Some(id) => tcx.type_of(id).instantiate_identity().skip_norm_wip(), None => return Lazy, }; if (matches!(name, sym::is_empty | sym::len) || name.as_str().starts_with("as_")) && have_one_arg { if matches!( - cx.tcx.crate_name(fn_id.krate), + tcx.crate_name(fn_id.krate), sym::std | sym::core | sym::alloc | sym::proc_macro ) { Eager @@ -71,22 +71,20 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: // Due to the limited operations on these types functions should be fairly cheap. if def.variants().iter().flat_map(|v| v.fields.iter()).any(|x| { matches!( - cx.tcx - .type_of(x.did) + tcx.type_of(x.did) .instantiate_identity() .skip_norm_wip() .peel_refs() .kind(), ty::Param(_) ) - }) && all_predicates_of(cx.tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() { - ty::ClauseKind::Trait(pred) => cx.tcx.trait_def(pred.trait_ref.def_id).is_marker, + }) && all_predicates_of(tcx, fn_id).all(|(pred, _)| match pred.kind().skip_binder() { + ty::ClauseKind::Trait(pred) => tcx.trait_def(pred.trait_ref.def_id).is_marker, _ => true, }) && subs.types().all(|x| matches!(x.peel_refs().kind(), ty::Param(_))) { // Limit the function to either `(self) -> bool` or `(&self) -> bool` - match &**cx - .tcx + match &**tcx .fn_sig(fn_id) .instantiate_identity() .skip_norm_wip() @@ -104,14 +102,12 @@ fn fn_eagerness(cx: &LateContext<'_>, fn_id: DefId, name: Symbol, have_one_arg: } } -fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> bool { +fn res_has_significant_drop(res: Res, ecx: &ConstEvalCtxt<'_>, e: &Expr<'_>) -> bool { if let Res::Def(DefKind::Ctor(..) | DefKind::Variant | DefKind::Enum | DefKind::Struct, _) | Res::SelfCtor(_) | Res::SelfTyAlias { .. } = res { - cx.typeck_results() - .expr_ty(e) - .has_significant_drop(cx.tcx, cx.typing_env()) + ecx.typeck.expr_ty(e).has_significant_drop(ecx.tcx, ecx.typing_env) } else { false } @@ -119,12 +115,12 @@ fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> boo #[expect(clippy::too_many_lines)] fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessSuggestion { - struct V<'cx, 'tcx> { - cx: &'cx LateContext<'tcx>, + struct V<'tcx> { + ecx: ConstEvalCtxt<'tcx>, eagerness: EagernessSuggestion, } - impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { + impl<'tcx> Visitor<'tcx> for V<'tcx> { fn visit_expr(&mut self, e: &'tcx Expr<'_>) { use EagernessSuggestion::{ForceNoChange, Lazy, NoChange}; if self.eagerness == ForceNoChange { @@ -134,8 +130,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // Autoderef through a user-defined `Deref` impl can have side-effects, // so don't suggest changing it. if self - .cx - .typeck_results() + .ecx + .typeck .expr_adjustments(e) .iter() .any(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)))) @@ -152,58 +148,62 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS .. }, args, - ) => match self.cx.qpath_res(path, hir_id) { + ) => match self.ecx.typeck.qpath_res(path, hir_id) { res @ (Res::Def(DefKind::Ctor(..) | DefKind::Variant, _) | Res::SelfCtor(_)) => { - if res_has_significant_drop(res, self.cx, e) { + if res_has_significant_drop(res, &self.ecx, e) { self.eagerness = ForceNoChange; return; } }, - Res::Def(_, id) if self.cx.tcx.is_promotable_const_fn(id) => (), + Res::Def(_, id) if self.ecx.tcx.is_promotable_const_fn(id) => (), // No need to walk the arguments here, `is_const_evaluatable` already did - Res::Def(..) if is_const_evaluatable(self.cx, e) => { + Res::Def(..) if is_const_evaluatable(self.ecx.tcx, self.ecx.typeck, e) => { self.eagerness |= NoChange; return; }, Res::Def(_, id) => match path { QPath::Resolved(_, p) => { - self.eagerness |= - fn_eagerness(self.cx, id, p.segments.last().unwrap().ident.name, !args.is_empty()); + self.eagerness |= fn_eagerness( + self.ecx.tcx, + id, + p.segments.last().unwrap().ident.name, + !args.is_empty(), + ); }, QPath::TypeRelative(_, name) => { - self.eagerness |= fn_eagerness(self.cx, id, name.ident.name, !args.is_empty()); + self.eagerness |= fn_eagerness(self.ecx.tcx, id, name.ident.name, !args.is_empty()); }, }, _ => self.eagerness = Lazy, }, // No need to walk the arguments here, `is_const_evaluatable` already did - ExprKind::MethodCall(..) if is_const_evaluatable(self.cx, e) => { + ExprKind::MethodCall(..) if is_const_evaluatable(self.ecx.tcx, self.ecx.typeck, e) => { self.eagerness |= NoChange; return; }, #[expect(clippy::match_same_arms)] // arm pattern can't be merged due to `ref`, see rust#105778 ExprKind::Struct(path, ..) => { - if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) { + if res_has_significant_drop(self.ecx.typeck.qpath_res(path, e.hir_id), &self.ecx, e) { self.eagerness = ForceNoChange; return; } }, ExprKind::Path(ref path) => { - if res_has_significant_drop(self.cx.qpath_res(path, e.hir_id), self.cx, e) { + if res_has_significant_drop(self.ecx.typeck.qpath_res(path, e.hir_id), &self.ecx, e) { self.eagerness = ForceNoChange; return; } }, ExprKind::MethodCall(name, ..) => { self.eagerness |= self - .cx - .typeck_results() + .ecx + .typeck .type_dependent_def_id(e.hir_id) - .map_or(Lazy, |id| fn_eagerness(self.cx, id, name.ident.name, true)); + .map_or(Lazy, |id| fn_eagerness(self.ecx.tcx, id, name.ident.name, true)); }, ExprKind::Index(_, e, _) => { - let ty = self.cx.typeck_results().expr_ty_adjusted(e); - if is_copy(self.cx, ty) && !ty.is_ref() { + let ty = self.ecx.typeck.expr_ty_adjusted(e); + if self.ecx.tcx.type_is_copy_modulo_regions(self.ecx.typing_env, ty) && !ty.is_ref() { self.eagerness |= NoChange; } else { self.eagerness = Lazy; @@ -211,24 +211,19 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS }, // `-i32::MIN` panics with overflow checks - ExprKind::Unary(UnOp::Neg, right) if ConstEvalCtxt::new(self.cx).eval(right).is_none() => { + ExprKind::Unary(UnOp::Neg, right) if self.ecx.eval(right).is_none() => { self.eagerness |= NoChange; }, // Custom `Deref` impl might have side effects - ExprKind::Unary(UnOp::Deref, e) - if self.cx.typeck_results().expr_ty(e).builtin_deref(true).is_none() => - { + ExprKind::Unary(UnOp::Deref, e) if self.ecx.typeck.expr_ty(e).builtin_deref(true).is_none() => { self.eagerness |= NoChange; }, // Dereferences should be cheap, but dereferencing a raw pointer earlier may not be safe. - ExprKind::Unary(UnOp::Deref, e) if !self.cx.typeck_results().expr_ty(e).is_raw_ptr() => (), + ExprKind::Unary(UnOp::Deref, e) if !self.ecx.typeck.expr_ty(e).is_raw_ptr() => (), ExprKind::Unary(UnOp::Deref, _) => self.eagerness |= NoChange, ExprKind::Unary(_, e) - if matches!( - self.cx.typeck_results().expr_ty(e).kind(), - ty::Bool | ty::Int(_) | ty::Uint(_), - ) => {}, + if matches!(self.ecx.typeck.expr_ty(e).kind(), ty::Bool | ty::Int(_) | ty::Uint(_),) => {}, // `>>` and `<<` panic when the right-hand side is greater than or equal to the number of bits in the // type of the left-hand side, or is negative. @@ -236,18 +231,16 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // overflow with constants, the compiler emits an error for it and the programmer will have to fix it. // Thus, we would realistically only delay the lint. ExprKind::Binary(op, _, right) - if matches!(op.node, BinOpKind::Shl | BinOpKind::Shr) - && ConstEvalCtxt::new(self.cx).eval(right).is_none() => + if matches!(op.node, BinOpKind::Shl | BinOpKind::Shr) && self.ecx.eval(right).is_none() => { self.eagerness |= NoChange; }, ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Div | BinOpKind::Rem) - && let right_ty = self.cx.typeck_results().expr_ty(right) - && let ecx = ConstEvalCtxt::new(self.cx) - && let left = ecx.eval(left) - && let right = ecx.eval(right).and_then(|c| c.int_value(self.cx.tcx, right_ty)) + && let right_ty = self.ecx.typeck.expr_ty(right) + && let left = self.ecx.eval(left) + && let right = self.ecx.eval(right).and_then(|c| c.int_value(self.ecx.tcx, right_ty)) && matches!( (left, right), // `1 / x`: x might be zero @@ -265,16 +258,14 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // error and it's good to have the eagerness warning up front when the user fixes the logic error. ExprKind::Binary(op, left, right) if matches!(op.node, BinOpKind::Add | BinOpKind::Sub | BinOpKind::Mul) - && !self.cx.typeck_results().expr_ty(e).is_floating_point() - && let ecx = ConstEvalCtxt::new(self.cx) - && (ecx.eval(left).is_none() || ecx.eval(right).is_none()) => + && !self.ecx.typeck.expr_ty(e).is_floating_point() + && (self.ecx.eval(left).is_none() || self.ecx.eval(right).is_none()) => { self.eagerness |= NoChange; }, ExprKind::Binary(_, lhs, rhs) - if self.cx.typeck_results().expr_ty(lhs).is_primitive() - && self.cx.typeck_results().expr_ty(rhs).is_primitive() => {}, + if self.ecx.typeck.expr_ty(lhs).is_primitive() && self.ecx.typeck.expr_ty(rhs).is_primitive() => {}, // Can't be moved into a closure ExprKind::Break(..) @@ -322,7 +313,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS } let mut v = V { - cx, + ecx: ConstEvalCtxt::new(cx), eagerness: EagernessSuggestion::Eager, }; v.visit_expr(e); diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs index 38724a1053f65..050e43344d0c4 100644 --- a/src/tools/clippy/clippy_utils/src/hir_utils.rs +++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs @@ -1,6 +1,6 @@ use crate::consts::ConstEvalCtxt; use crate::macros::macro_backtrace; -use crate::source::{SpanRange, SpanRangeExt, walk_span_to_context}; +use crate::source::{SpanExt, SpanRange, walk_span_to_context}; use crate::{sym, tokenize_with_text}; use core::mem; use rustc_ast::ast; @@ -1036,7 +1036,7 @@ fn reduce_exprkind<'hir>( // `{}` => `()` ([], None) if block.span.ctxt() != eval_ctxt - || block.span.check_source_text(cx, |src| { + || block.span.check_text(cx, |src| { tokenize(src, FrontmatterAllowed::No) .map(|t| t.kind) .filter(|t| { diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index c208b8b97506f..b6afbc3fba03d 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -46,7 +46,6 @@ mod check_proc_macro; pub mod comparisons; pub mod consts; pub mod diagnostics; -pub mod disallowed_profiles; pub mod eager_or_lazy; pub mod higher; mod hir_utils; @@ -116,14 +115,14 @@ use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; use rustc_span::symbol::{Ident, Symbol, kw}; use rustc_span::{InnerSpan, Span, SyntaxContext}; -use source::{SpanRangeExt, walk_span_to_context}; +use source::{SpanExt, walk_span_to_context}; use visitors::{Visitable, for_each_unconsumed_temporary}; use crate::ast_utils::unordered_over; -use crate::consts::ConstEvalCtxt; use crate::higher::Range; use crate::msrvs::Msrv; use crate::res::{MaybeDef, MaybeQPath, MaybeResPath}; +use crate::source::HasSourceMap; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; @@ -1329,59 +1328,23 @@ pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool { }) } -/// Checks whether the given `Expr` is a range equivalent to a `RangeFull`. -/// -/// For the lower bound, this means that: -/// - either there is none -/// - or it is the smallest value that can be represented by the range's integer type -/// -/// For the upper bound, this means that: -/// - either there is none -/// - or it is the largest value that can be represented by the range's integer type and is -/// inclusive -/// - or it is a call to some container's `len` method and is exclusive, and the range is passed to -/// a method call on that same container (e.g. `v.drain(..v.len())`) -/// -/// If the given `Expr` is not some kind of range, the function returns `false`. -pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool { - let ty = cx.typeck_results().expr_ty(expr); +/// Checks whether the given `Expr` is a range over the entire container. +pub fn is_full_collection_range(cx: &LateContext<'_>, container: Option, expr: &Expr<'_>) -> bool { if let Some(Range { start, end, limits, .. }) = Range::hir(cx, expr) { - let start_is_none_or_min = start.is_none_or(|start| { - if let rustc_ty::Adt(_, subst) = ty.kind() - && let bnd_ty = subst.type_at(0) - && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start) - { - start_const.is_numeric_min(cx.tcx, bnd_ty) - } else { - false - } - }); - let end_is_none_or_max = end.is_none_or(|end| match limits { - RangeLimits::Closed => { - if let rustc_ty::Adt(_, subst) = ty.kind() - && let bnd_ty = subst.type_at(0) - && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end) - { - end_const.is_numeric_max(cx.tcx, bnd_ty) - } else { - false - } - }, - RangeLimits::HalfOpen => { - if let Some(container_path) = container_path - && let ExprKind::MethodCall(name, self_arg, [], _) = end.kind - && name.ident.name == sym::len - && let ExprKind::Path(QPath::Resolved(None, path)) = self_arg.kind + start.is_none_or(|start| is_integer_literal(start, 0)) + && end.is_none_or(|end| { + if limits == RangeLimits::HalfOpen + && let Some(container) = container + && let ExprKind::MethodCall(seg, recv, [], _) = end.kind { - container_path.res == path.res + seg.ident.name == sym::len && recv.res_local_id() == Some(container) } else { false } - }, - }); - return start_is_none_or_min && end_is_none_or_max; + }) + } else { + false } - false } /// Checks whether the given expression is a constant literal of the given value. @@ -2880,8 +2843,8 @@ pub fn tokenize_with_text(s: &str) -> impl Iterator bool { - span.check_source_text(cx, |snippet| { +pub fn span_contains_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> bool { + span.check_text(sm, |snippet| { tokenize(snippet, FrontmatterAllowed::No).any(|token| { matches!( token.kind, @@ -2895,8 +2858,8 @@ pub fn span_contains_comment(cx: &impl source::HasSession, span: Span) -> bool { /// token, including comments unless `skip_comments` is set. /// This is useful to determine if there are any actual code tokens in the span that are omitted in /// the late pass, such as platform-specific code. -pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, skip_comments: bool) -> bool { - span.check_source_text(cx, |snippet| { +pub fn span_contains_non_whitespace<'sm>(sm: impl HasSourceMap<'sm>, span: Span, skip_comments: bool) -> bool { + span.check_text(sm, |snippet| { tokenize_with_text(snippet).any(|(token, _, _)| match token { TokenKind::Whitespace => false, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. } => !skip_comments, @@ -2904,18 +2867,19 @@ pub fn span_contains_non_whitespace(cx: &impl source::HasSession, span: Span, sk }) }) } + /// Returns all the comments a given span contains /// /// Comments are returned wrapped with their relevant delimiters -pub fn span_extract_comment(cx: &impl source::HasSession, span: Span) -> String { - span_extract_comments(cx, span).join("\n") +pub fn span_extract_comment<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> String { + span_extract_comments(sm, span).join("\n") } /// Returns all the comments a given span contains. /// /// Comments are returned wrapped with their relevant delimiters. -pub fn span_extract_comments(cx: &impl source::HasSession, span: Span) -> Vec { - span.with_source_text(cx, |snippet| { +pub fn span_extract_comments<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Vec { + span.with_source_text(sm, |snippet| { tokenize_with_text(snippet) .filter(|(t, ..)| matches!(t, TokenKind::BlockComment { .. } | TokenKind::LineComment { .. })) .map(|(_, s, _)| s.to_string()) diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs index 5151351ed2ade..d9b34ebfe21e4 100644 --- a/src/tools/clippy/clippy_utils/src/msrvs.rs +++ b/src/tools/clippy/clippy_utils/src/msrvs.rs @@ -3,8 +3,9 @@ use rustc_ast::Attribute; use rustc_ast::attr::AttributeExt; use rustc_attr_parsing::parse_version; use rustc_data_structures::smallvec::SmallVec; -use rustc_hir::RustcVersion; +use rustc_hir::{HirId, RustcVersion}; use rustc_lint::LateContext; +use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_span::Symbol; use serde::Deserialize; @@ -26,7 +27,7 @@ msrv_aliases! { 1,97,0 { ISOLATE_LOWEST_ONE } 1,93,0 { VEC_DEQUE_POP_BACK_IF, VEC_DEQUE_POP_FRONT_IF } 1,91,0 { DURATION_FROM_MINUTES_HOURS } - 1,88,0 { LET_CHAINS } + 1,88,0 { LET_CHAINS, AS_CHUNKS } 1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF, INTEGER_SIGN_CAST } 1,86,0 { VEC_POP_IF } 1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL, WAKER_NOOP } @@ -118,16 +119,29 @@ impl Msrv { /// nodes for that attribute, prefer to run this check after cheaper pattern matching operations pub fn current(self, cx: &LateContext<'_>) -> Option { if SEEN_MSRV_ATTR.load(Ordering::Relaxed) { - let start = cx.last_node_with_lint_attrs; - if let Some(msrv_attr) = once(start) - .chain(cx.tcx.hir_parent_id_iter(start)) - .find_map(|id| parse_attrs(cx.tcx.sess, cx.tcx.hir_attrs(id))) - { - return Some(msrv_attr); - } + self.for_attrs(cx.tcx, cx.last_node_with_lint_attrs) + } else { + self.0 } + } - self.0 + /// Returns the MSRV at the specified node + /// + /// If the crate being linted uses an `#[clippy::msrv]` attribute this will search the parent + /// nodes for that attribute, prefer to run this check after cheaper pattern matching operations + pub fn at(self, tcx: TyCtxt<'_>, node: HirId) -> Option { + if SEEN_MSRV_ATTR.load(Ordering::Relaxed) { + self.for_attrs(tcx, node) + } else { + self.0 + } + } + + fn for_attrs(self, tcx: TyCtxt<'_>, node: HirId) -> Option { + once(node) + .chain(tcx.hir_parent_id_iter(node)) + .find_map(|id| parse_attrs(tcx.sess, tcx.hir_attrs(id))) + .or(self.0) } /// Checks if a required version from [this module](self) is met at the current node @@ -138,6 +152,14 @@ impl Msrv { self.current(cx).is_none_or(|msrv| msrv >= required) } + /// Checks if a required version from [this module](self) is met at the specified node + /// + /// If the crate being linted uses an `#[clippy::msrv]` attribute this will search the parent + /// nodes for that attribute, prefer to run this check after cheaper pattern matching operations + pub fn meets_at(self, tcx: TyCtxt<'_>, node: HirId, required: RustcVersion) -> bool { + self.at(tcx, node).is_none_or(|msrv| msrv >= required) + } + pub fn read_cargo(&mut self, sess: &Session) { let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION") .ok() diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs index ddb3c2d6cb4a5..d0316a77c8ff4 100644 --- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs +++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs @@ -6,9 +6,8 @@ use crate::msrvs::{self, Msrv}; use hir::LangItem; use rustc_const_eval::check_consts::ConstCx; -use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::{RustcVersion, StableSince}; +use rustc_hir::{self as hir, HirId, RustcVersion, StableSince}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::Obligation; use rustc_lint::LateContext; @@ -420,14 +419,17 @@ fn check_terminator<'tcx>( /// Checks if the given `def_id` is a stable const fn, in respect to the given MSRV. pub fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bool { - cx.tcx.is_const_fn(def_id) - && cx - .tcx + is_stable_const_fn_at(cx.tcx, cx.last_node_with_lint_attrs, def_id, msrv) +} + +/// Checks if the given `def_id` is a stable const fn, in respect to the given MSRV. +pub fn is_stable_const_fn_at(tcx: TyCtxt<'_>, node: HirId, def_id: DefId, msrv: Msrv) -> bool { + tcx.is_const_fn(def_id) + && tcx .lookup_const_stability(def_id) .or_else(|| { - cx.tcx - .trait_of_assoc(def_id) - .and_then(|trait_def_id| cx.tcx.lookup_const_stability(trait_def_id)) + tcx.trait_of_assoc(def_id) + .and_then(|trait_def_id| tcx.lookup_const_stability(trait_def_id)) }) .is_none_or(|const_stab| { if let rustc_hir::StabilityLevel::Stable { since, .. } = const_stab.level { @@ -441,10 +443,10 @@ pub fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bo StableSince::Err(_) => return false, }; - msrv.meets(cx, const_stab_rust_version) + msrv.meets_at(tcx, node, const_stab_rust_version) } else { // Unstable const fn, check if the feature is enabled. - cx.tcx.features().enabled(const_stab.feature) && msrv.current(cx).is_none() + tcx.features().enabled(const_stab.feature) && msrv.at(tcx, node).is_none() } }) } diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs index 02d502f8b67f8..171276f997d39 100644 --- a/src/tools/clippy/clippy_utils/src/source.rs +++ b/src/tools/clippy/clippy_utils/src/source.rs @@ -20,27 +20,38 @@ use std::borrow::Cow; use std::fmt; use std::ops::{Deref, Index, Range}; -pub trait HasSession { - fn sess(&self) -> &Session; +pub trait HasSourceMap<'sm>: Copy { + #[must_use] + fn source_map(self) -> &'sm SourceMap; } -impl HasSession for Session { - fn sess(&self) -> &Session { +impl<'sm> HasSourceMap<'sm> for &'sm SourceMap { + #[inline] + fn source_map(self) -> &'sm SourceMap { self } } -impl HasSession for TyCtxt<'_> { - fn sess(&self) -> &Session { - self.sess +impl<'sm> HasSourceMap<'sm> for &'sm Session { + #[inline] + fn source_map(self) -> &'sm SourceMap { + self.source_map() } } -impl HasSession for EarlyContext<'_> { - fn sess(&self) -> &Session { - ::rustc_lint::LintContext::sess(self) +impl<'sm> HasSourceMap<'sm> for TyCtxt<'sm> { + #[inline] + fn source_map(self) -> &'sm SourceMap { + self.sess.source_map() } } -impl HasSession for LateContext<'_> { - fn sess(&self) -> &Session { - self.tcx.sess() +impl<'sm> HasSourceMap<'sm> for &'sm EarlyContext<'_> { + #[inline] + fn source_map(self) -> &'sm SourceMap { + ::rustc_lint::LintContext::sess(self).source_map() + } +} +impl<'sm> HasSourceMap<'sm> for &LateContext<'sm> { + #[inline] + fn source_map(self) -> &'sm SourceMap { + self.tcx.sess.source_map() } } @@ -95,39 +106,39 @@ impl IntoSpan for Range { } } -pub trait SpanRangeExt: SpanRange { +pub trait SpanExt: SpanRange { /// Attempts to get a handle to the source text. Returns `None` if either the span is malformed, /// or the source text is not accessible. - fn get_source_text(self, cx: &impl HasSession) -> Option { - get_source_range(cx.sess().source_map(), self.into_range()).and_then(SourceText::new) + fn get_text<'sm>(self, sm: impl HasSourceMap<'sm>) -> Option { + get_source_range(sm.source_map(), self.into_range()).and_then(SourceText::new) } /// Gets the source file, and range in the file, of the given span. Returns `None` if the span /// extends through multiple files, or is malformed. - fn get_source_range(self, cx: &impl HasSession) -> Option { - get_source_range(cx.sess().source_map(), self.into_range()) + fn get_source_range<'sm>(self, sm: impl HasSourceMap<'sm>) -> Option { + get_source_range(sm.source_map(), self.into_range()) } /// Calls the given function with the source text referenced and returns the value. Returns /// `None` if the source text cannot be retrieved. - fn with_source_text(self, cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str) -> T) -> Option { - with_source_text(cx.sess().source_map(), self.into_range(), f) + fn with_source_text<'sm, T>(self, sm: impl HasSourceMap<'sm>, f: impl for<'a> FnOnce(&'a str) -> T) -> Option { + with_source_text(sm.source_map(), self.into_range(), f) } /// Checks if the referenced source text satisfies the given predicate. Returns `false` if the /// source text cannot be retrieved. - fn check_source_text(self, cx: &impl HasSession, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool { - self.with_source_text(cx, pred).unwrap_or(false) + fn check_text<'sm>(self, sm: impl HasSourceMap<'sm>, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool { + self.with_source_text(sm, pred).unwrap_or(false) } /// Calls the given function with the both the text of the source file and the referenced range, /// and returns the value. Returns `None` if the source text cannot be retrieved. - fn with_source_text_and_range( + fn with_source_text_and_range<'sm, T>( self, - cx: &impl HasSession, + sm: impl HasSourceMap<'sm>, f: impl for<'a> FnOnce(&'a str, Range) -> T, ) -> Option { - with_source_text_and_range(cx.sess().source_map(), self.into_range(), f) + with_source_text_and_range(sm.source_map(), self.into_range(), f) } /// Calls the given function with the both the text of the source file and the referenced range, @@ -135,12 +146,12 @@ pub trait SpanRangeExt: SpanRange { /// retrieved, or no result is returned. /// /// The new range must reside within the same source file. - fn map_range( + fn map_range<'sm>( self, - cx: &impl HasSession, + sm: impl HasSourceMap<'sm>, f: impl for<'a> FnOnce(&'a SourceFile, &'a str, Range) -> Option>, ) -> Option> { - map_range(cx.sess().source_map(), self.into_range(), f) + map_range(sm.source_map(), self.into_range(), f) } /// Extends the range to include all preceding whitespace characters. @@ -156,16 +167,16 @@ pub trait SpanRangeExt: SpanRange { /// /// When the range points to `foo`, suggesting to remove the range after it's been extended will /// cause the `)` to be placed inside the line comment as `( // Some comment)`. - fn with_leading_whitespace(self, cx: &impl HasSession) -> Range { - with_leading_whitespace(cx.sess().source_map(), self.into_range()) + fn with_leading_whitespace<'sm>(self, sm: impl HasSourceMap<'sm>) -> Range { + with_leading_whitespace(sm.source_map(), self.into_range()) } /// Trims the leading whitespace from the range. - fn trim_start(self, cx: &impl HasSession) -> Range { - trim_start(cx.sess().source_map(), self.into_range()) + fn trim_start<'sm>(self, sm: impl HasSourceMap<'sm>) -> Range { + trim_start(sm.source_map(), self.into_range()) } } -impl SpanRangeExt for T {} +impl SpanExt for T {} /// Handle to a range of text in a source file. pub struct SourceText(SourceFileRange); @@ -353,15 +364,15 @@ impl SourceFileRange { } /// Like [`snippet_block`], but add braces if the expr is not an `ExprKind::Block` with no label. -pub fn expr_block( - sess: &impl HasSession, +pub fn expr_block<'sm>( + sm: impl HasSourceMap<'sm>, expr: &Expr<'_>, outer: SyntaxContext, default: &str, indent_relative_to: Option, app: &mut Applicability, ) -> String { - let (code, from_macro) = snippet_block_with_context(sess, expr.span, outer, default, indent_relative_to, app); + let (code, from_macro) = snippet_block_with_context(sm, expr.span, outer, default, indent_relative_to, app); if !from_macro && let ExprKind::Block(block, None) = expr.kind && block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) @@ -386,13 +397,13 @@ pub fn expr_block( /// let x = (); /// // ^^^^^^^^^^ /// ``` -pub fn first_line_of_span(sess: &impl HasSession, span: Span) -> Span { - first_char_in_first_line(sess, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) +pub fn first_line_of_span<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Span { + first_char_in_first_line(sm, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos)) } -fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option { - let line_span = line_span(sess, span); - snippet_opt(sess, line_span).and_then(|snip| { +fn first_char_in_first_line<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option { + let line_span = line_span(sm, span); + snippet_opt(sm, line_span).and_then(|snip| { snip.find(|c: char| !c.is_whitespace()) .map(|pos| line_span.lo() + BytePos::from_usize(pos)) }) @@ -407,9 +418,9 @@ fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option Span { +fn line_span<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Span { let span = original_sp(span, DUMMY_SP); - let SourceFileAndLine { sf, line } = sess.sess().source_map().lookup_line(span.lo()).unwrap(); + let SourceFileAndLine { sf, line } = sm.source_map().lookup_line(span.lo()).unwrap(); let line_start = sf.lines()[line]; let line_start = sf.absolute_position(line_start); span.with_lo(line_start) @@ -423,13 +434,13 @@ fn line_span(sess: &impl HasSession, span: Span) -> Span { /// let x = (); /// // ^^ -- will return 4 /// ``` -pub fn indent_of(sess: &impl HasSession, span: Span) -> Option { - snippet_opt(sess, line_span(sess, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) +pub fn indent_of<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option { + snippet_opt(sm, line_span(sm, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace())) } /// Gets a snippet of the indentation of the line of a span -pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option { - snippet_opt(sess, line_span(sess, span)).map(|mut s| { +pub fn snippet_indent<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option { + snippet_opt(sm, line_span(sm, span)).map(|mut s| { let len = s.len() - s.trim_start().len(); s.truncate(len); s @@ -441,8 +452,8 @@ pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option { // sources that the user has no control over. // For some reason these attributes don't have any expansion info on them, so // we have to check it this way until there is a better way. -pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool { - if let Some(snippet) = snippet_opt(sess, span) +pub fn is_present_in_source<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> bool { + if let Some(snippet) = snippet_opt(sm, span) && snippet.is_empty() { return false; @@ -534,8 +545,8 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option, /// snippet(cx, span1, "..") // -> "value" /// snippet(cx, span2, "..") // -> "Vec::new()" /// ``` -pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> { - snippet_opt(sess, span).map_or_else(|| Cow::Borrowed(default), From::from) +pub fn snippet<'a, 'sm>(sm: impl HasSourceMap<'sm>, span: Span, default: &'a str) -> Cow<'a, str> { + snippet_opt(sm, span).map_or_else(|| Cow::Borrowed(default), From::from) } /// Same as [`snippet`], but it adapts the applicability level by following rules: @@ -547,17 +558,17 @@ pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow< /// /// If the span might realistically contain a macro call (e.g. `vec![]`), consider using /// [`snippet_with_context`] instead. -pub fn snippet_with_applicability<'a>( - sess: &impl HasSession, +pub fn snippet_with_applicability<'a, 'sm>( + sm: impl HasSourceMap<'sm>, span: Span, default: &'a str, applicability: &mut Applicability, ) -> Cow<'a, str> { - snippet_with_applicability_sess(sess.sess(), span, default, applicability) + snippet_with_applicability_sm(sm.source_map(), span, default, applicability) } -fn snippet_with_applicability_sess<'a>( - sess: &Session, +fn snippet_with_applicability_sm<'a>( + sm: &SourceMap, span: Span, default: &'a str, applicability: &mut Applicability, @@ -565,7 +576,7 @@ fn snippet_with_applicability_sess<'a>( if *applicability != Applicability::Unspecified && span.from_expansion() { *applicability = Applicability::MaybeIncorrect; } - if let Some(t) = snippet_opt(sess, span) { + if let Some(t) = snippet_opt(sm, span) { Cow::Owned(t) } else { if *applicability == Applicability::MachineApplicable { @@ -576,8 +587,8 @@ fn snippet_with_applicability_sess<'a>( } /// Converts a span to a code snippet. Returns `None` if not available. -pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option { - sess.sess().source_map().span_to_snippet(span).ok() +pub fn snippet_opt<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Option { + sm.source_map().span_to_snippet(span).ok() } /// Converts a span (from a block) to a code snippet if available, otherwise use default. @@ -614,37 +625,42 @@ pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option { /// } // aligned with `if` /// ``` /// Note that the first line of the snippet always has 0 indentation. -pub fn snippet_block(sess: &impl HasSession, span: Span, default: &str, indent_relative_to: Option) -> String { - let snip = snippet(sess, span, default); - let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); +pub fn snippet_block<'sm>( + sm: impl HasSourceMap<'sm>, + span: Span, + default: &str, + indent_relative_to: Option, +) -> String { + let snip = snippet(sm, span, default); + let indent = indent_relative_to.and_then(|s| indent_of(sm, s)); reindent_multiline(&snip, true, indent) } /// Same as [`snippet_block`], but adapts the applicability level by the rules of /// [`snippet_with_applicability`]. -pub fn snippet_block_with_applicability( - sess: &impl HasSession, +pub fn snippet_block_with_applicability<'sm>( + sm: impl HasSourceMap<'sm>, span: Span, default: &str, indent_relative_to: Option, applicability: &mut Applicability, ) -> String { - let snip = snippet_with_applicability(sess, span, default, applicability); - let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); + let snip = snippet_with_applicability(sm, span, default, applicability); + let indent = indent_relative_to.and_then(|s| indent_of(sm, s)); reindent_multiline(&snip, true, indent) } /// Combination of [`snippet_block`] and [`snippet_with_context`]. -pub fn snippet_block_with_context( - sess: &impl HasSession, +pub fn snippet_block_with_context<'sm>( + sm: impl HasSourceMap<'sm>, span: Span, outer: SyntaxContext, default: &str, indent_relative_to: Option, app: &mut Applicability, ) -> (String, bool) { - let (snip, from_macro) = snippet_with_context(sess, span, outer, default, app); - let indent = indent_relative_to.and_then(|s| indent_of(sess, s)); + let (snip, from_macro) = snippet_with_context(sm, span, outer, default, app); + let indent = indent_relative_to.and_then(|s| indent_of(sm, s)); (reindent_multiline(&snip, true, indent), from_macro) } @@ -658,18 +674,18 @@ pub fn snippet_block_with_context( /// correctly get a snippet of `vec![]`. /// /// This will also return whether or not the snippet is a macro call. -pub fn snippet_with_context<'a>( - sess: &impl HasSession, +pub fn snippet_with_context<'a, 'sm>( + sm: impl HasSourceMap<'sm>, span: Span, outer: SyntaxContext, default: &'a str, applicability: &mut Applicability, ) -> (Cow<'a, str>, bool) { - snippet_with_context_sess(sess.sess(), span, outer, default, applicability) + snippet_with_context_sm(sm.source_map(), span, outer, default, applicability) } -fn snippet_with_context_sess<'a>( - sess: &Session, +fn snippet_with_context_sm<'a>( + sm: &SourceMap, span: Span, outer: SyntaxContext, default: &'a str, @@ -677,10 +693,7 @@ fn snippet_with_context_sess<'a>( ) -> (Cow<'a, str>, bool) { // If it is just range desugaring, use the desugaring span since it may include parenthesis. if span.desugaring_kind() == Some(DesugaringKind::RangeExpr) && span.parent_callsite().unwrap().ctxt() == outer { - return ( - snippet_with_applicability_sess(sess, span, default, applicability), - false, - ); + return (snippet_with_applicability_sm(sm, span, default, applicability), false); } let (span, is_macro_call) = walk_span_to_context(span, outer).map_or_else( @@ -696,7 +709,7 @@ fn snippet_with_context_sess<'a>( ); ( - snippet_with_applicability_sess(sess, span, default, applicability), + snippet_with_applicability_sm(sm, span, default, applicability), is_macro_call, ) } @@ -759,15 +772,15 @@ pub fn trim_span(sm: &SourceMap, span: Span) -> Span { /// writeln!(o, "") -> writeln!(o, "") /// ^^ ^^^^ /// ``` -pub fn expand_past_previous_comma(sess: &impl HasSession, span: Span) -> Span { - let extended = sess.sess().source_map().span_extend_to_prev_char(span, ',', true); +pub fn expand_past_previous_comma<'sm>(sm: impl HasSourceMap<'sm>, span: Span) -> Span { + let extended = sm.source_map().span_extend_to_prev_char(span, ',', true); extended.with_lo(extended.lo() - BytePos(1)) } /// Converts `expr` to a `char` literal if it's a `str` literal containing a single /// character (or a single byte with `ascii_only`) -pub fn str_literal_to_char_literal( - sess: &impl HasSession, +pub fn str_literal_to_char_literal<'sm>( + sm: impl HasSourceMap<'sm>, expr: &Expr<'_>, applicability: &mut Applicability, ascii_only: bool, @@ -782,7 +795,7 @@ pub fn str_literal_to_char_literal( } && len == 1 { - let snip = snippet_with_applicability(sess, expr.span, string, applicability); + let snip = snippet_with_applicability(sm, expr.span, string, applicability); let ch = if let StrStyle::Raw(nhash) = style { let nhash = nhash as usize; // for raw string: r##"a"## diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 59e98cdc7bef8..6053788c82b13 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -176,6 +176,8 @@ generate! { checked_sub, child_id, child_kill, + chunks_exact, + chunks_exact_mut, clamp, clippy_utils, clone_into, @@ -209,8 +211,6 @@ generate! { deprecated_in_future, deref_mut_method, diagnostics, - disallowed_profile, - disallowed_profiles, disallowed_types, drain, dump, diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 8cded245fac22..c363cc96c7e19 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -3,7 +3,7 @@ #![allow(clippy::module_name_repetitions)] use core::ops::ControlFlow; -use rustc_abi::VariantIdx; +use rustc_abi::{BackendRepr, FieldsShape, VariantIdx, Variants}; use rustc_ast::ast::Mutability; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir as hir; @@ -17,7 +17,7 @@ use rustc_middle::mir::ConstValue; use rustc_middle::mir::interpret::Scalar; use rustc_middle::traits::EvaluationResult; use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; -use rustc_middle::ty::layout::ValidityRequirement; +use rustc_middle::ty::layout::{LayoutError, LayoutOf, TyAndLayout}; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, GenericArgKind, GenericArgsRef, IntTy, ProjectionAliasTy, Region, RegionKind, TraitRef, Ty, TyCtxt, @@ -34,7 +34,7 @@ use std::{debug_assert_matches, iter, mem}; use crate::paths::{PathNS, lookup_path_str}; use crate::res::{MaybeDef, MaybeQPath}; -use crate::sym; +use crate::{over, sym}; mod type_certainty; pub use type_certainty::expr_type_is_certain; @@ -486,8 +486,8 @@ pub fn peel_n_ty_refs(mut ty: Ty<'_>, n: usize) -> (Ty<'_>, Option) /// and `false` for: /// - `Result` and `Result` pub fn same_type_modulo_regions<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { - match (&a.kind(), &b.kind()) { - (&ty::Adt(did_a, args_a), &ty::Adt(did_b, args_b)) => { + match (a.kind(), b.kind()) { + (ty::Adt(did_a, args_a), ty::Adt(did_b, args_b)) => { if did_a != did_b { return false; } @@ -500,35 +500,112 @@ pub fn same_type_modulo_regions<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { _ => true, }) }, + (ty::Ref(_, a, mut_a), ty::Ref(_, b, mut_b)) => mut_a == mut_b && same_type_modulo_regions(*a, *b), + (ty::Tuple(as_), ty::Tuple(bs)) => over(as_, bs, |a, b| same_type_modulo_regions(*a, *b)), + (ty::Array(a, na), ty::Array(b, nb)) => na == nb && same_type_modulo_regions(*a, *b), _ => a == b, } } /// Checks if a given type looks safe to be uninitialized. pub fn is_uninit_value_valid_for_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { - let typing_env = cx.typing_env().with_post_analysis_normalized(cx.tcx); - cx.tcx - .check_validity_requirement((ValidityRequirement::Uninit, typing_env.as_query_input(ty))) - .unwrap_or_else(|_| is_uninit_value_valid_for_ty_fallback(cx, ty)) + match cx.layout_of(ty) { + Ok(layout) => is_uninit_value_valid_for_layout(cx, layout), + // The type layout is either not concrete enough yet or too large, fall back to structural check instead + Err(LayoutError::TooGeneric(_) | LayoutError::SizeOverflow(_)) => is_uninit_value_valid_for_ty_fallback(cx, ty), + Err(_) => false, + } +} + +fn is_uninit_value_valid_for_layout<'tcx>(cx: &LateContext<'tcx>, layout: TyAndLayout<'tcx>) -> bool { + // ZSTs contribute no bytes to the vector buffer + if layout.layout.is_zst() { + return true; + } + + match layout.layout.backend_repr { + BackendRepr::Scalar(s) => s.is_uninit_valid(), + BackendRepr::ScalarPair(a, b) => a.is_uninit_valid() && b.is_uninit_valid(), + BackendRepr::SimdVector { element, count } => count == 0 || element.is_uninit_valid(), + BackendRepr::SimdScalableVector { element, .. } => element.is_uninit_valid(), + // Here validity is determined by the structural fields instead. + BackendRepr::Memory { .. } => match &layout.layout.variants { + Variants::Single { .. } => match &layout.layout.fields { + FieldsShape::Primitive => { + debug_assert!(false, "Both Scalar primitives and ! should be handled above."); + false + }, + // Arrays are valid if empty, or if their elements are valid. + FieldsShape::Array { count, .. } => { + if *count == 0 { + true + } else { + is_uninit_value_valid_for_layout(cx, layout.field(cx, 0)) + } + }, + // Structs like types are valid only if all fields are valid. + FieldsShape::Arbitrary { offsets, .. } => { + (0..offsets.len()).all(|i| is_uninit_value_valid_for_layout(cx, layout.field(cx, i))) + }, + // Unions are valid if at least one field is valid. + FieldsShape::Union(count) => { + (0..count.get()).any(|i| is_uninit_value_valid_for_layout(cx, layout.field(cx, i))) + }, + }, + // Types with no valid variants must be uninhabited + Variants::Empty => true, + // Enum like with multiple inhabited variants have a discriminant, they cannot be uninitialized. + Variants::Multiple { .. } => false, + }, + } } -/// A fallback for polymorphic types, which are not supported by `check_validity_requirement`. +/// Fallback for polymorphic types where `layout_of` fails fn is_uninit_value_valid_for_ty_fallback<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool { + let typing_env = cx.typing_env().with_post_analysis_normalized(cx.tcx); + match *ty.kind() { // The array length may be polymorphic, let's try the inner type. - ty::Array(component, _) => is_uninit_value_valid_for_ty(cx, component), + ty::Array(component, len) => { + // Zero-length arrays are always valid + if len.try_to_target_usize(cx.tcx) == Some(0) { + return true; + } + is_uninit_value_valid_for_ty(cx, component) + }, // Peek through tuples and try their fallbacks. ty::Tuple(types) => types.iter().all(|ty| is_uninit_value_valid_for_ty(cx, ty)), - // Unions are always fine right now. - // This includes MaybeUninit, the main way people use uninitialized memory. - ty::Adt(adt, _) if adt.is_union() => true, + // For Unions, check if any field is uninit + ty::Adt(adt, args) if adt.is_union() => adt.all_fields().any(|field| { + let unnormalized_field_ty = field.ty(cx.tcx, args); + let Ok(field_ty) = cx.tcx.try_normalize_erasing_regions(typing_env, unnormalized_field_ty) else { + debug_assert!( + false, + "failed to normalize field type `{unnormalized_field_ty:?}`, ParamEnv is likely set incorrectly." + ); + return false; + }; + is_uninit_value_valid_for_ty(cx, field_ty) + }), // Types (e.g. `UnsafeCell>`) that recursively contain only types that can be uninit // can themselves be uninit too. - // This purposefully ignores enums as they may have a discriminant that can't be uninit. - ty::Adt(adt, args) if adt.is_struct() => adt - .all_fields() - .all(|field| is_uninit_value_valid_for_ty(cx, field.ty(cx.tcx, args).skip_norm_wip())), - // For the rest, conservatively assume that they cannot be uninit. + // This also applies for single variant enums, whose validity is determined by their fields. + ty::Adt(adt, args) if adt.is_struct() || adt.variants().len() == 1 => adt.all_fields().all(|field| { + let unnormalized_field_ty = field.ty(cx.tcx, args); + let Ok(field_ty) = cx.tcx.try_normalize_erasing_regions(typing_env, unnormalized_field_ty) else { + debug_assert!( + false, + "failed to normalize field type `{unnormalized_field_ty:?}`, ParamEnv is likely set incorrectly." + ); + return false; + }; + + is_uninit_value_valid_for_ty(cx, field_ty) + }), + // Without a usable whole type layout, + // conservatively reject remaining enum cases + ty::Adt(adt, _) if adt.is_enum() => false, + // Conservatively reject remaining types _ => false, } } @@ -1049,9 +1126,13 @@ pub fn make_projection<'tcx>( assert_generic_args_match(tcx, assoc_item.def_id, args); let kind = if let DefKind::Impl { of_trait: false } = tcx.def_kind(tcx.parent(assoc_item.def_id)) { - ty::AliasTyKind::Inherent { def_id: assoc_item.def_id } + ty::AliasTyKind::Inherent { + def_id: assoc_item.def_id, + } } else { - ty::AliasTyKind::Projection { def_id: assoc_item.def_id } + ty::AliasTyKind::Projection { + def_id: assoc_item.def_id, + } }; Some(AliasTy::new_from_args(tcx, kind, args)) @@ -1335,6 +1416,14 @@ pub fn option_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option` or a `Result` and return its argument type (`T`) if it is. +pub fn option_or_result_arg_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option> { + match ty.kind() { + ty::Adt(adt, args) if matches!(adt.opt_diag_name(cx), Some(sym::Option | sym::Result)) => Some(args.type_at(0)), + _ => None, + } +} + /// Check if a Ty<'_> of `Iterator` contains any mutable access to non-owning types by checking if /// it contains fields of mutable references or pointers, or references/pointers to non-`Freeze` /// types, or `PhantomData` types containing any of the previous. This can be used to check whether diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs index 4a1d24024915b..d644cd5a4aace 100644 --- a/src/tools/clippy/clippy_utils/src/usage.rs +++ b/src/tools/clippy/clippy_utils/src/usage.rs @@ -195,7 +195,7 @@ pub fn contains_return_break_continue_macro(expression: &Expr<'_>) -> bool { } pub fn local_used_in<'tcx>(cx: &LateContext<'tcx>, local_id: HirId, v: impl Visitable<'tcx>) -> bool { - for_each_expr(cx, v, |e| { + for_each_expr(cx.tcx, v, |e| { if e.res_local_id() == Some(local_id) { ControlFlow::Break(()) } else { @@ -220,7 +220,7 @@ pub fn local_used_after_expr(cx: &LateContext<'_>, local_id: HirId, after: &Expr let loop_start = get_enclosing_loop_or_multi_call_closure(cx, after).map(|e| e.hir_id); let mut past_expr = false; - for_each_expr(cx, block, |e| { + for_each_expr(cx.tcx, block, |e| { if past_expr { if e.res_local_id() == Some(local_id) { ControlFlow::Break(()) diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs index 28449a75a8fc5..ca1fa57cffc10 100644 --- a/src/tools/clippy/clippy_utils/src/visitors.rs +++ b/src/tools/clippy/clippy_utils/src/visitors.rs @@ -1,6 +1,6 @@ use crate::get_enclosing_block; use crate::msrvs::Msrv; -use crate::qualify_min_const_fn::is_stable_const_fn; +use crate::qualify_min_const_fn::is_stable_const_fn_at; use crate::res::MaybeResPath; use crate::ty::needs_ordered_drop; use core::ops::ControlFlow; @@ -8,8 +8,8 @@ use rustc_ast::visit::{VisitorResult, try_visit}; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr}; use rustc_hir::{ - self as hir, AmbigArg, AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, - ItemKind, LetExpr, Pat, QPath, Stmt, StructTailExpr, UnOp, UnsafeSource, + self as hir, AmbigArg, AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, CRATE_HIR_ID, Expr, ExprKind, HirId, + ItemId, ItemKind, LetExpr, Pat, QPath, Stmt, StructTailExpr, UnOp, UnsafeSource, }; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter; @@ -145,7 +145,7 @@ pub fn for_each_expr_without_closures<'tcx, B, C: Continue>( /// Calls the given function once for each expression contained. This will enter bodies, but not /// nested items. pub fn for_each_expr<'tcx, B, C: Continue>( - cx: &LateContext<'tcx>, + tcx: TyCtxt<'tcx>, node: impl Visitable<'tcx>, f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow, ) -> Option { @@ -188,7 +188,7 @@ pub fn for_each_expr<'tcx, B, C: Continue>( ControlFlow::Continue(()) } } - let mut v = V { tcx: cx.tcx, f }; + let mut v = V { tcx, f }; node.visit(&mut v).break_value() } @@ -299,7 +299,7 @@ where /// Checks if the given resolved path is used in the given body. pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { - for_each_expr(cx, cx.tcx.hir_body(body).value, |e| { + for_each_expr(cx.tcx, cx.tcx.hir_body(body).value, |e| { if let ExprKind::Path(p) = &e.kind && cx.qpath_res(p, e.hir_id) == res { @@ -312,7 +312,7 @@ pub fn is_res_used(cx: &LateContext<'_>, res: Res, body: BodyId) -> bool { /// Checks if the given local is used. pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tcx>, id: HirId) -> bool { - for_each_expr(cx, visitable, |e| { + for_each_expr(cx.tcx, visitable, |e| { if e.res_local_id() == Some(id) { ControlFlow::Break(()) } else { @@ -322,89 +322,69 @@ pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tc .is_some() } -/// Checks if the given expression is a constant. -pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool { - struct V<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - } - - impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> { - type Result = ControlFlow<()>; - type NestedFilter = intravisit::nested_filter::None; - - fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result { - match e.kind { - ExprKind::ConstBlock(_) => return ControlFlow::Continue(()), - ExprKind::Call( - &Expr { - kind: ExprKind::Path(ref p), - hir_id, - .. - }, - _, - ) if self - .cx - .qpath_res(p, hir_id) - .opt_def_id() - .is_some_and(|id| is_stable_const_fn(self.cx, id, Msrv::default())) => {}, - ExprKind::MethodCall(..) - if self - .cx - .typeck_results() - .type_dependent_def_id(e.hir_id) - .is_some_and(|id| is_stable_const_fn(self.cx, id, Msrv::default())) => {}, - ExprKind::Binary(_, lhs, rhs) - if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty() - && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {}, - ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => (), - ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (), - ExprKind::Index(base, _, _) - if matches!( - self.cx.typeck_results().expr_ty(base).peel_refs().kind(), - ty::Slice(_) | ty::Array(..) - ) => {}, - ExprKind::Path(ref p) - if matches!( - self.cx.qpath_res(p, e.hir_id), - Res::Def( - DefKind::Const { .. } - | DefKind::AssocConst { .. } - | DefKind::AnonConst - | DefKind::ConstParam - | DefKind::Ctor(..) - | DefKind::Fn - | DefKind::AssocFn, - _ - ) | Res::SelfCtor(_) - ) => {}, - - ExprKind::AddrOf(..) - | ExprKind::Array(_) - | ExprKind::Block(..) - | ExprKind::Cast(..) - | ExprKind::DropTemps(_) - | ExprKind::Field(..) - | ExprKind::If(..) - | ExprKind::Let(..) - | ExprKind::Lit(_) - | ExprKind::Match(..) - | ExprKind::Repeat(..) - | ExprKind::Struct(..) - | ExprKind::Tup(_) - | ExprKind::Type(..) - | ExprKind::UnsafeBinderCast(..) => (), - - _ => { - return ControlFlow::Break(()); +/// Checks if the given expression can be evaluated as a constant at the specified node +pub fn is_const_evaluatable<'tcx>(tcx: TyCtxt<'tcx>, typeck: &'tcx TypeckResults<'tcx>, e: &'tcx Expr<'_>) -> bool { + for_each_expr(tcx, e, move |e| { + match e.kind { + ExprKind::ConstBlock(_) => return ControlFlow::Continue(Descend::No), + ExprKind::Call( + &Expr { + kind: ExprKind::Path(ref p), + hir_id, + .. }, - } + _, + ) if typeck + .qpath_res(p, hir_id) + .opt_def_id() + .is_some_and(|id| is_stable_const_fn_at(tcx, CRATE_HIR_ID, id, Msrv::default())) => {}, + ExprKind::MethodCall(..) + if typeck + .type_dependent_def_id(e.hir_id) + .is_some_and(|id| is_stable_const_fn_at(tcx, CRATE_HIR_ID, id, Msrv::default())) => {}, + ExprKind::Binary(_, lhs, rhs) + if typeck.expr_ty(lhs).peel_refs().is_primitive_ty() + && typeck.expr_ty(rhs).peel_refs().is_primitive_ty() => {}, + ExprKind::Unary(UnOp::Deref, e) if typeck.expr_ty(e).is_raw_ptr() => (), + ExprKind::Unary(_, e) if typeck.expr_ty(e).peel_refs().is_primitive_ty() => (), + ExprKind::Index(base, _, _) + if matches!(typeck.expr_ty(base).peel_refs().kind(), ty::Slice(_) | ty::Array(..)) => {}, + ExprKind::Path(ref p) + if matches!( + typeck.qpath_res(p, e.hir_id), + Res::Def( + DefKind::Const { .. } + | DefKind::AssocConst { .. } + | DefKind::AnonConst + | DefKind::ConstParam + | DefKind::Ctor(..) + | DefKind::Fn + | DefKind::AssocFn, + _ + ) | Res::SelfCtor(_) + ) => {}, + + ExprKind::AddrOf(..) + | ExprKind::Array(_) + | ExprKind::Block(..) + | ExprKind::Cast(..) + | ExprKind::DropTemps(_) + | ExprKind::Field(..) + | ExprKind::If(..) + | ExprKind::Let(..) + | ExprKind::Lit(_) + | ExprKind::Match(..) + | ExprKind::Repeat(..) + | ExprKind::Struct(..) + | ExprKind::Tup(_) + | ExprKind::Type(..) + | ExprKind::UnsafeBinderCast(..) => {}, - walk_expr(self, e) + _ => return ControlFlow::Break(()), } - } - - let mut v = V { cx }; - v.visit_expr(e).is_continue() + ControlFlow::Continue(Descend::Yes) + }) + .is_none() } /// Checks if the given expression performs an unsafe operation outside of an unsafe block. @@ -785,7 +765,7 @@ pub fn local_used_once<'tcx>( ) -> Option<&'tcx Expr<'tcx>> { let mut expr = None; - let cf = for_each_expr(cx, visitable, |e| { + let cf = for_each_expr(cx.tcx, visitable, |e| { if e.res_local_id() == Some(id) && expr.replace(e).is_some() { ControlFlow::Break(()) } else { diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml index 3afa9da1f947d..ac044327aafe9 100644 --- a/src/tools/clippy/rust-toolchain.toml +++ b/src/tools/clippy/rust-toolchain.toml @@ -1,6 +1,6 @@ [toolchain] # begin autogenerated nightly -channel = "nightly-2026-06-11" +channel = "nightly-2026-06-25" # end autogenerated nightly components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"] profile = "minimal" diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/clippy.toml b/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/clippy.toml deleted file mode 100644 index c77f5658ae69c..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/clippy.toml +++ /dev/null @@ -1,13 +0,0 @@ -disallowed-methods = [ - { path = "std::mem::drop" } -] - -[profiles.forward_pass] -disallowed-methods = [ - { path = "alloc::vec::Vec::push", reason = "push is forbidden in forward profile" } -] - -[profiles.export] -disallowed-methods = [ - { path = "core::option::Option::unwrap" } -] diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.rs b/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.rs deleted file mode 100644 index 07bb22e05d6f7..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.rs +++ /dev/null @@ -1,78 +0,0 @@ -#![warn(clippy::disallowed_methods)] -#![allow( - unused, - clippy::no_effect, - clippy::needless_borrow, - clippy::vec_init_then_push, - clippy::unnecessary_literal_unwrap -)] - -fn default_violation() { - let value = String::from("test"); - std::mem::drop(value); //~ ERROR: use of a disallowed method `std::mem::drop` -} - -#[expect(clippy::disallowed_methods)] -fn expected_violation() { - let value = String::from("test"); - std::mem::drop(value); -} - -#[clippy::disallowed_profile("forward_pass")] -fn forward_profile() { - let mut values = Vec::new(); - values.push(1); //~ ERROR: use of a disallowed method `alloc::vec::Vec::push` (profile: forward_pass) -} - -#[clippy::disallowed_profile("export")] -fn export_profile() { - let value = Some(1); - value.unwrap(); //~ ERROR: use of a disallowed method `core::option::Option::unwrap` (profile: export) -} - -#[clippy::disallowed_profile("unknown_profile")] -//~^ ERROR: unknown profile `unknown_profile` for -//~| ERROR: unknown profile `unknown_profile` for -fn unknown_profile() { - let mut values = Vec::new(); - values.push(1); - // unknown profile falls back to the default list - std::mem::drop(values); //~ ERROR: use of a disallowed method `std::mem::drop` -} - -#[clippy::disallowed_profiles("forward_pass", "export")] -fn merged_profiles() { - let mut values = Vec::new(); - values.push(1); //~ ERROR: use of a disallowed method `alloc::vec::Vec::push` (profile: forward_pass) - let value = Some(1); - value.unwrap(); //~ ERROR: use of a disallowed method `core::option::Option::unwrap` (profile: export) -} - -// `#[expect(clippy::disallowed_methods)]` silences the body warning and the unknown-profile -// warning tagged under `DISALLOWED_METHODS`, but not the one tagged under `DISALLOWED_TYPES`. -#[expect(clippy::disallowed_methods)] -#[clippy::disallowed_profile("unknown_profile_expect_before")] -//~^ ERROR: unknown profile `unknown_profile_expect_before` for `clippy::disallowed_types` -fn expect_before_unknown_profile() { - let value = String::from("test"); - std::mem::drop(value); -} - -#[clippy::disallowed_profile("unknown_profile_expect_after")] -//~^ ERROR: unknown profile `unknown_profile_expect_after` for `clippy::disallowed_types` -#[expect(clippy::disallowed_methods)] -fn expect_after_unknown_profile() { - let value = String::from("test"); - std::mem::drop(value); -} - -fn main() { - default_violation(); - expected_violation(); - forward_profile(); - export_profile(); - unknown_profile(); - merged_profiles(); - expect_before_unknown_profile(); - expect_after_unknown_profile(); -} diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.stderr b/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.stderr deleted file mode 100644 index 5f64b1bf4499b..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_methods/main.stderr +++ /dev/null @@ -1,72 +0,0 @@ -error: use of a disallowed method `std::mem::drop` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:12:5 - | -LL | std::mem::drop(value); - | ^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-methods` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` - -error: use of a disallowed method `alloc::vec::Vec::push` (profile: forward_pass) - --> tests/ui-toml/disallowed_profiles_methods/main.rs:24:12 - | -LL | values.push(1); - | ^^^^ - | - = note: push is forbidden in forward profile - -error: use of a disallowed method `core::option::Option::unwrap` (profile: export) - --> tests/ui-toml/disallowed_profiles_methods/main.rs:30:11 - | -LL | value.unwrap(); - | ^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_profile` for `clippy::disallowed_methods` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:33:30 - | -LL | #[clippy::disallowed_profile("unknown_profile")] - | ^^^^^^^^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_profile` for `clippy::disallowed_types` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:33:30 - | -LL | #[clippy::disallowed_profile("unknown_profile")] - | ^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-types` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_types)]` - -error: use of a disallowed method `std::mem::drop` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:40:5 - | -LL | std::mem::drop(values); - | ^^^^^^^^^^^^^^ - -error: use of a disallowed method `alloc::vec::Vec::push` (profile: forward_pass) - --> tests/ui-toml/disallowed_profiles_methods/main.rs:46:12 - | -LL | values.push(1); - | ^^^^ - | - = note: push is forbidden in forward profile - -error: use of a disallowed method `core::option::Option::unwrap` (profile: export) - --> tests/ui-toml/disallowed_profiles_methods/main.rs:48:11 - | -LL | value.unwrap(); - | ^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_profile_expect_before` for `clippy::disallowed_types` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:54:30 - | -LL | #[clippy::disallowed_profile("unknown_profile_expect_before")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_profile_expect_after` for `clippy::disallowed_types` - --> tests/ui-toml/disallowed_profiles_methods/main.rs:61:30 - | -LL | #[clippy::disallowed_profile("unknown_profile_expect_after")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 10 previous errors - diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/clippy.toml b/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/clippy.toml deleted file mode 100644 index 3c47b2afe26fe..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/clippy.toml +++ /dev/null @@ -1,13 +0,0 @@ -disallowed-types = [ - { path = "std::rc::Rc" } -] - -[profiles.forward_pass] -disallowed-types = [ - { path = "std::cell::RefCell", reason = "Prefer shared references" } -] - -[profiles.export] -disallowed-types = [ - { path = "std::sync::Mutex" } -] diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.rs b/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.rs deleted file mode 100644 index 0161417dd6022..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.rs +++ /dev/null @@ -1,61 +0,0 @@ -#![warn(clippy::disallowed_types)] -#![allow(dead_code)] - -use std::rc::Rc; //~ ERROR: use of a disallowed type `std::rc::Rc` -use std::sync::Mutex; - -struct Wrapper; - -fn default_type() { - let _value: Rc = todo!(); //~ ERROR: use of a disallowed type `std::rc::Rc` -} - -#[clippy::disallowed_profile("forward_pass")] -fn forward_profile() { - let _value: std::cell::RefCell = todo!(); //~ ERROR: use of a disallowed type `std::cell::RefCell` (profile: forward_pass) -} - -#[clippy::disallowed_profile("export")] -fn export_profile() { - let _value: Mutex = todo!(); //~ ERROR: use of a disallowed type `std::sync::Mutex` (profile: export) -} - -#[clippy::disallowed_profile("unknown_type_profile")] -//~^ ERROR: unknown profile `unknown_type_profile` for -//~| ERROR: unknown profile `unknown_type_profile` for -fn unknown_profile() { - let _other = 1u32; - let _fallback: Rc = todo!(); //~ ERROR: use of a disallowed type `std::rc::Rc` -} - -#[clippy::disallowed_profiles("forward_pass", "export")] -fn merged_profiles() { - let _value: std::cell::RefCell = todo!(); //~ ERROR: use of a disallowed type `std::cell::RefCell` (profile: forward_pass) - let _other: Mutex = todo!(); //~ ERROR: use of a disallowed type `std::sync::Mutex` (profile: export) -} - -// `#[expect(clippy::disallowed_types)]` silences the body warning and the unknown-profile -// warning tagged under `DISALLOWED_TYPES`, but not one tagged under `DISALLOWED_METHODS`. -#[expect(clippy::disallowed_types)] -#[clippy::disallowed_profile("unknown_type_profile_expect_before")] -//~^ ERROR: unknown profile `unknown_type_profile_expect_before` for `clippy::disallowed_methods` -fn expect_before_unknown_profile() { - let _value: Rc = todo!(); -} - -#[clippy::disallowed_profile("unknown_type_profile_expect_after")] -//~^ ERROR: unknown profile `unknown_type_profile_expect_after` for `clippy::disallowed_methods` -#[expect(clippy::disallowed_types)] -fn expect_after_unknown_profile() { - let _value: Rc = todo!(); -} - -fn main() { - default_type(); - forward_profile(); - export_profile(); - unknown_profile(); - merged_profiles(); - expect_before_unknown_profile(); - expect_after_unknown_profile(); -} diff --git a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.stderr b/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.stderr deleted file mode 100644 index 68d63e218c0de..0000000000000 --- a/src/tools/clippy/tests/ui-toml/disallowed_profiles_types/main.stderr +++ /dev/null @@ -1,78 +0,0 @@ -error: use of a disallowed type `std::rc::Rc` - --> tests/ui-toml/disallowed_profiles_types/main.rs:4:1 - | -LL | use std::rc::Rc; - | ^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-types` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_types)]` - -error: use of a disallowed type `std::rc::Rc` - --> tests/ui-toml/disallowed_profiles_types/main.rs:10:17 - | -LL | let _value: Rc = todo!(); - | ^^^^^^^ - -error: use of a disallowed type `std::cell::RefCell` (profile: forward_pass) - --> tests/ui-toml/disallowed_profiles_types/main.rs:15:17 - | -LL | let _value: std::cell::RefCell = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: Prefer shared references - -error: use of a disallowed type `std::sync::Mutex` (profile: export) - --> tests/ui-toml/disallowed_profiles_types/main.rs:20:17 - | -LL | let _value: Mutex = todo!(); - | ^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_type_profile` for `clippy::disallowed_methods` - --> tests/ui-toml/disallowed_profiles_types/main.rs:23:30 - | -LL | #[clippy::disallowed_profile("unknown_type_profile")] - | ^^^^^^^^^^^^^^^^^^^^^^ - | - = note: `-D clippy::disallowed-methods` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]` - -error: `clippy::disallowed_profile` references unknown profile `unknown_type_profile` for `clippy::disallowed_types` - --> tests/ui-toml/disallowed_profiles_types/main.rs:23:30 - | -LL | #[clippy::disallowed_profile("unknown_type_profile")] - | ^^^^^^^^^^^^^^^^^^^^^^ - -error: use of a disallowed type `std::rc::Rc` - --> tests/ui-toml/disallowed_profiles_types/main.rs:28:20 - | -LL | let _fallback: Rc = todo!(); - | ^^^^^^^ - -error: use of a disallowed type `std::cell::RefCell` (profile: forward_pass) - --> tests/ui-toml/disallowed_profiles_types/main.rs:33:17 - | -LL | let _value: std::cell::RefCell = todo!(); - | ^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: Prefer shared references - -error: use of a disallowed type `std::sync::Mutex` (profile: export) - --> tests/ui-toml/disallowed_profiles_types/main.rs:34:17 - | -LL | let _other: Mutex = todo!(); - | ^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_type_profile_expect_before` for `clippy::disallowed_methods` - --> tests/ui-toml/disallowed_profiles_types/main.rs:40:30 - | -LL | #[clippy::disallowed_profile("unknown_type_profile_expect_before")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: `clippy::disallowed_profile` references unknown profile `unknown_type_profile_expect_after` for `clippy::disallowed_methods` - --> tests/ui-toml/disallowed_profiles_types/main.rs:46:30 - | -LL | #[clippy::disallowed_profile("unknown_type_profile_expect_after")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 11 previous errors - diff --git a/src/tools/clippy/tests/ui-toml/needless_late_init/clippy.toml b/src/tools/clippy/tests/ui-toml/needless_late_init/clippy.toml new file mode 100644 index 0000000000000..2ff5a13e893be --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/needless_late_init/clippy.toml @@ -0,0 +1 @@ +check-grouped-late-init = false diff --git a/src/tools/clippy/tests/ui-toml/needless_late_init/needless_late_init.rs b/src/tools/clippy/tests/ui-toml/needless_late_init/needless_late_init.rs new file mode 100644 index 0000000000000..5a5e657b295c3 --- /dev/null +++ b/src/tools/clippy/tests/ui-toml/needless_late_init/needless_late_init.rs @@ -0,0 +1,64 @@ +//@check-pass + +fn main() {} + +fn issue16330() { + let a; + let b; + if true { + a = 1; + b = 2; + } else { + a = 3; + b = 4; + } + + let a; + let mut b = 1; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + + let b; + { + let a; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + } + + let a; + let b; + let c; + match 1 { + 1 => { + a = 1; + b = 2; + c = 3; + }, + _ if false => { + a = 4; + b = 5; + c = 6; + }, + _ => { + a = 7; + b = 8; + c = 9; + }, + } +} diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr index ba930d094fc19..528e2f1089c04 100644 --- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr +++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr @@ -33,6 +33,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect avoid-breaking-exported-api await-holding-invalid-types cargo-ignore-publish + check-grouped-late-init check-incompatible-msrv-in-tests check-inconsistent-struct-field-initializers check-private-items @@ -70,7 +71,6 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect module-items-ordered-within-groupings msrv pass-by-value-size-limit - profiles pub-underscore-fields-behavior recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline @@ -135,6 +135,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect avoid-breaking-exported-api await-holding-invalid-types cargo-ignore-publish + check-grouped-late-init check-incompatible-msrv-in-tests check-inconsistent-struct-field-initializers check-private-items @@ -172,7 +173,6 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect module-items-ordered-within-groupings msrv pass-by-value-size-limit - profiles pub-underscore-fields-behavior recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline @@ -237,6 +237,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni avoid-breaking-exported-api await-holding-invalid-types cargo-ignore-publish + check-grouped-late-init check-incompatible-msrv-in-tests check-inconsistent-struct-field-initializers check-private-items @@ -274,7 +275,6 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni module-items-ordered-within-groupings msrv pass-by-value-size-limit - profiles pub-underscore-fields-behavior recursive-self-in-type-definitions semicolon-inside-block-ignore-singleline diff --git a/src/tools/clippy/tests/ui/author/issue_17240.rs b/src/tools/clippy/tests/ui/author/issue_17240.rs new file mode 100644 index 0000000000000..591a19907720a --- /dev/null +++ b/src/tools/clippy/tests/ui/author/issue_17240.rs @@ -0,0 +1,8 @@ +//@ check-pass +// Ensure that a proper body is used when printing paths (`x`) +// if the attribute is placed on an item. +#[clippy::author] +fn main() { + let x = 42i32; + _ = -x; +} diff --git a/src/tools/clippy/tests/ui/author/issue_17240.stdout b/src/tools/clippy/tests/ui/author/issue_17240.stdout new file mode 100644 index 0000000000000..81c927180ce1c --- /dev/null +++ b/src/tools/clippy/tests/ui/author/issue_17240.stdout @@ -0,0 +1,20 @@ +if let ExprKind::Block(block, None) = expr.kind + && block.stmts.len() == 2 + && let StmtKind::Let(local) = block.stmts[0].kind + && let Some(init) = local.init + && let ExprKind::Lit(ref lit) = init.kind + && let LitKind::Int(42, LitIntType::Signed(IntTy::I32)) = lit.node + && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind + && name.as_str() == "x" + && let StmtKind::Semi(e) = block.stmts[1].kind + && let ExprKind::Block(block1, None) = e.kind + && block1.stmts.len() == 1 + && let StmtKind::Let(local1) = block1.stmts[0].kind + && let Some(init1) = local1.init + && let ExprKind::Unary(UnOp::Neg, inner) = init1.kind + && let PatKind::Wild = local1.pat.kind + && block1.expr.is_none() + && block.expr.is_none() +{ + // report your lint here +} diff --git a/src/tools/clippy/tests/ui/auxiliary/unnecessary_unwrap_unchecked_helper.rs b/src/tools/clippy/tests/ui/auxiliary/unnecessary_unwrap_unchecked_helper.rs new file mode 100644 index 0000000000000..4cc9ac80692d2 --- /dev/null +++ b/src/tools/clippy/tests/ui/auxiliary/unnecessary_unwrap_unchecked_helper.rs @@ -0,0 +1,17 @@ +#![allow(unused, clippy::missing_safety_doc)] + +pub fn lol() -> Option { + Some(0) +} + +pub unsafe fn lol_unchecked() -> u32 { + 0 +} + +pub fn kek() -> Option { + Some(0) +} + +unsafe fn kek_unchecked() -> u32 { + 0 +} diff --git a/src/tools/clippy/tests/ui/by_ref_peekable_peek.1.fixed b/src/tools/clippy/tests/ui/by_ref_peekable_peek.1.fixed new file mode 100644 index 0000000000000..e40b745245568 --- /dev/null +++ b/src/tools/clippy/tests/ui/by_ref_peekable_peek.1.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::by_ref_peekable_peek)] + +struct S; + +impl S { + fn by_ref(&mut self) -> impl Iterator { + std::iter::empty() + } +} + +macro_rules! mac { + ($x:expr) => { + $x.by_ref().peekable().peek() + }; +} + +fn with_non_clone_parameter(i: &mut impl Iterator) { + // This won't suggest `.clone().next().as_ref()` as `i` is not `Clone` + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator(a: Vec) { + let mut i = a.into_iter(); + let _: Option<&i32> = i.clone().next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator_from_macro() { + macro_rules! mac { + () => { + [1, 2, 3].into_iter() + }; + } + let mut i = mac!(); + let _: Option<&i32> = i.clone().next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn main() { + let mut iter = [1, 2, 3].into_iter(); + let _: Option<&i32> = iter.clone().next().as_ref(); + //~^ by_ref_peekable_peek + #[expect(clippy::needless_borrow)] + #[allow(clippy::unnecessary_mut_passed)] // For the `.clone().next().as_ref()` suggestion + let _: Option<&i32> = (&mut iter).clone().next().as_ref(); + //~^ by_ref_peekable_peek + + // Do not lint if `by_ref()` is not the one on `Iterator` + let _: Option<&i32> = S.by_ref().peekable().peek(); + + // Do not lint if coming from a macro, as we cannot ensure + // that all uses of `.by_ref()` will be `Iterator::by_ref()`. + let _: Option<&i32> = mac!(iter); +} diff --git a/src/tools/clippy/tests/ui/by_ref_peekable_peek.2.fixed b/src/tools/clippy/tests/ui/by_ref_peekable_peek.2.fixed new file mode 100644 index 0000000000000..b9b7cdeaf5922 --- /dev/null +++ b/src/tools/clippy/tests/ui/by_ref_peekable_peek.2.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::by_ref_peekable_peek)] + +struct S; + +impl S { + fn by_ref(&mut self) -> impl Iterator { + std::iter::empty() + } +} + +macro_rules! mac { + ($x:expr) => { + $x.by_ref().peekable().peek() + }; +} + +fn with_non_clone_parameter(i: &mut impl Iterator) { + // This won't suggest `.clone().next().as_ref()` as `i` is not `Clone` + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator(a: Vec) { + let mut i = a.into_iter(); + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator_from_macro() { + macro_rules! mac { + () => { + [1, 2, 3].into_iter() + }; + } + let mut i = mac!(); + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn main() { + let mut iter = [1, 2, 3].into_iter(); + let _: Option<&i32> = iter.next().as_ref(); + //~^ by_ref_peekable_peek + #[expect(clippy::needless_borrow)] + #[allow(clippy::unnecessary_mut_passed)] // For the `.clone().next().as_ref()` suggestion + let _: Option<&i32> = (&mut iter).next().as_ref(); + //~^ by_ref_peekable_peek + + // Do not lint if `by_ref()` is not the one on `Iterator` + let _: Option<&i32> = S.by_ref().peekable().peek(); + + // Do not lint if coming from a macro, as we cannot ensure + // that all uses of `.by_ref()` will be `Iterator::by_ref()`. + let _: Option<&i32> = mac!(iter); +} diff --git a/src/tools/clippy/tests/ui/by_ref_peekable_peek.3.fixed b/src/tools/clippy/tests/ui/by_ref_peekable_peek.3.fixed new file mode 100644 index 0000000000000..67256fc86887c --- /dev/null +++ b/src/tools/clippy/tests/ui/by_ref_peekable_peek.3.fixed @@ -0,0 +1,55 @@ +#![warn(clippy::by_ref_peekable_peek)] + +struct S; + +impl S { + fn by_ref(&mut self) -> impl Iterator { + std::iter::empty() + } +} + +macro_rules! mac { + ($x:expr) => { + $x.by_ref().peekable().peek() + }; +} + +fn with_non_clone_parameter(i: &mut impl Iterator) { + // This won't suggest `.clone().next().as_ref()` as `i` is not `Clone` + let _: Option<&i32> = i.next().as_ref(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator(a: Vec) { + let mut i = a.into_iter().peekable(); + let _: Option<&i32> = i.peek(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator_from_macro() { + macro_rules! mac { + () => { + [1, 2, 3].into_iter() + }; + } + let mut i = mac!().peekable(); + let _: Option<&i32> = i.peek(); + //~^ by_ref_peekable_peek +} + +fn main() { + let mut iter = [1, 2, 3].into_iter().peekable(); + let _: Option<&i32> = iter.peek(); + //~^ by_ref_peekable_peek + #[expect(clippy::needless_borrow)] + #[allow(clippy::unnecessary_mut_passed)] // For the `.clone().next().as_ref()` suggestion + let _: Option<&i32> = (&mut iter).clone().next().as_ref(); + //~^ by_ref_peekable_peek + + // Do not lint if `by_ref()` is not the one on `Iterator` + let _: Option<&i32> = S.by_ref().peekable().peek(); + + // Do not lint if coming from a macro, as we cannot ensure + // that all uses of `.by_ref()` will be `Iterator::by_ref()`. + let _: Option<&i32> = mac!(iter); +} diff --git a/src/tools/clippy/tests/ui/by_ref_peekable_peek.rs b/src/tools/clippy/tests/ui/by_ref_peekable_peek.rs new file mode 100644 index 0000000000000..d8ccb8b74fb64 --- /dev/null +++ b/src/tools/clippy/tests/ui/by_ref_peekable_peek.rs @@ -0,0 +1,55 @@ +#![warn(clippy::by_ref_peekable_peek)] + +struct S; + +impl S { + fn by_ref(&mut self) -> impl Iterator { + std::iter::empty() + } +} + +macro_rules! mac { + ($x:expr) => { + $x.by_ref().peekable().peek() + }; +} + +fn with_non_clone_parameter(i: &mut impl Iterator) { + // This won't suggest `.clone().next().as_ref()` as `i` is not `Clone` + let _: Option<&i32> = i.by_ref().peekable().peek(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator(a: Vec) { + let mut i = a.into_iter(); + let _: Option<&i32> = i.by_ref().peekable().peek(); + //~^ by_ref_peekable_peek +} + +fn with_cloneable_local_iterator_from_macro() { + macro_rules! mac { + () => { + [1, 2, 3].into_iter() + }; + } + let mut i = mac!(); + let _: Option<&i32> = i.by_ref().peekable().peek(); + //~^ by_ref_peekable_peek +} + +fn main() { + let mut iter = [1, 2, 3].into_iter(); + let _: Option<&i32> = iter.by_ref().peekable().peek(); + //~^ by_ref_peekable_peek + #[expect(clippy::needless_borrow)] + #[allow(clippy::unnecessary_mut_passed)] // For the `.clone().next().as_ref()` suggestion + let _: Option<&i32> = (&mut iter).by_ref().peekable().peek(); + //~^ by_ref_peekable_peek + + // Do not lint if `by_ref()` is not the one on `Iterator` + let _: Option<&i32> = S.by_ref().peekable().peek(); + + // Do not lint if coming from a macro, as we cannot ensure + // that all uses of `.by_ref()` will be `Iterator::by_ref()`. + let _: Option<&i32> = mac!(iter); +} diff --git a/src/tools/clippy/tests/ui/by_ref_peekable_peek.stderr b/src/tools/clippy/tests/ui/by_ref_peekable_peek.stderr new file mode 100644 index 0000000000000..5a284775e04f3 --- /dev/null +++ b/src/tools/clippy/tests/ui/by_ref_peekable_peek.stderr @@ -0,0 +1,101 @@ +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:19:27 + | +LL | let _: Option<&i32> = i.by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you might want to transform the iterator itself using `.peekable()` without using `.by_ref()` + = note: `-D clippy::by-ref-peekable-peek` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::by_ref_peekable_peek)]` +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.next().as_ref(); + | + +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:25:27 + | +LL | let _: Option<&i32> = i.by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: to peek the first item without advancing the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.clone().next().as_ref(); + | +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.next().as_ref(); + | +help: to make the iterator peekable, use + | +LL ~ let mut i = a.into_iter().peekable(); +LL ~ let _: Option<&i32> = i.peek(); + | + +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:36:27 + | +LL | let _: Option<&i32> = i.by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: to peek the first item without advancing the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.clone().next().as_ref(); + | +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = i.by_ref().peekable().peek(); +LL + let _: Option<&i32> = i.next().as_ref(); + | +help: to make the iterator peekable, use + | +LL ~ let mut i = mac!().peekable(); +LL ~ let _: Option<&i32> = i.peek(); + | + +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:42:27 + | +LL | let _: Option<&i32> = iter.by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: to peek the first item without advancing the underlying iterator, use + | +LL - let _: Option<&i32> = iter.by_ref().peekable().peek(); +LL + let _: Option<&i32> = iter.clone().next().as_ref(); + | +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = iter.by_ref().peekable().peek(); +LL + let _: Option<&i32> = iter.next().as_ref(); + | +help: to make the iterator peekable, use + | +LL ~ let mut iter = [1, 2, 3].into_iter().peekable(); +LL ~ let _: Option<&i32> = iter.peek(); + | + +error: calling `.by_ref().peekable().peek()` will advance the underlying iterator and consume its first output + --> tests/ui/by_ref_peekable_peek.rs:46:27 + | +LL | let _: Option<&i32> = (&mut iter).by_ref().peekable().peek(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: you might want to transform the iterator itself using `.peekable()` without using `.by_ref()` +help: to peek the first item without advancing the underlying iterator, use + | +LL - let _: Option<&i32> = (&mut iter).by_ref().peekable().peek(); +LL + let _: Option<&i32> = (&mut iter).clone().next().as_ref(); + | +help: to advance the underlying iterator, use + | +LL - let _: Option<&i32> = (&mut iter).by_ref().peekable().peek(); +LL + let _: Option<&i32> = (&mut iter).next().as_ref(); + | + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.rs b/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.rs new file mode 100644 index 0000000000000..0d5f4d06043d1 --- /dev/null +++ b/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.rs @@ -0,0 +1,43 @@ +#![warn(clippy::chunks_exact_to_as_chunks)] +#![allow(unused)] + +fn main() { + let slice = [1, 2, 3, 4, 5, 6, 7, 8]; + + // Should trigger lint - literal constant + let mut it = slice.chunks_exact(4); + //~^ chunks_exact_to_as_chunks + for chunk in it {} + + // Should trigger lint - const value + const CHUNK_SIZE: usize = 4; + let mut it = slice.chunks_exact(CHUNK_SIZE); + //~^ chunks_exact_to_as_chunks + for chunk in it {} + + // Should NOT trigger - runtime value + let size = 4; + let mut it = slice.chunks_exact(size); + for chunk in it {} + + // Should trigger lint - with remainder + let mut it = slice.chunks_exact(3); + //~^ chunks_exact_to_as_chunks + for chunk in &mut it {} + for e in it.remainder() {} + + // Should trigger - mutable variant + let mut arr = [1, 2, 3, 4, 5, 6, 7, 8]; + let mut it = arr.chunks_exact_mut(4); + //~^ chunks_exact_to_as_chunks + for chunk in it {} + + // Should NOT trigger - type must unify with another branch + let condition = true; + let y = 3; + let _ = if condition { + slice.chunks_exact(5) + } else { + slice.chunks_exact(y) + }; +} diff --git a/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.stderr b/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.stderr new file mode 100644 index 0000000000000..628752f9bae71 --- /dev/null +++ b/src/tools/clippy/tests/ui/chunks_exact_to_as_chunks.stderr @@ -0,0 +1,56 @@ +error: using `chunks_exact` with a constant chunk size + --> tests/ui/chunks_exact_to_as_chunks.rs:8:24 + | +LL | let mut it = slice.chunks_exact(4); + | ^^^^^^^^^^^^^^^ + | +help: consider using `as_chunks::<4>()` instead + --> tests/ui/chunks_exact_to_as_chunks.rs:8:24 + | +LL | let mut it = slice.chunks_exact(4); + | ^^^^^^^^^^^^^^^ + = note: you can access the chunks using `it.0.iter()`, and the remainder using `it.1` + = note: `-D clippy::chunks-exact-to-as-chunks` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::chunks_exact_to_as_chunks)]` + +error: using `chunks_exact` with a constant chunk size + --> tests/ui/chunks_exact_to_as_chunks.rs:14:24 + | +LL | let mut it = slice.chunks_exact(CHUNK_SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: consider using `as_chunks::()` instead + --> tests/ui/chunks_exact_to_as_chunks.rs:14:24 + | +LL | let mut it = slice.chunks_exact(CHUNK_SIZE); + | ^^^^^^^^^^^^^^^^^^^^^^^^ + = note: you can access the chunks using `it.0.iter()`, and the remainder using `it.1` + +error: using `chunks_exact` with a constant chunk size + --> tests/ui/chunks_exact_to_as_chunks.rs:24:24 + | +LL | let mut it = slice.chunks_exact(3); + | ^^^^^^^^^^^^^^^ + | +help: consider using `as_chunks::<3>()` instead + --> tests/ui/chunks_exact_to_as_chunks.rs:24:24 + | +LL | let mut it = slice.chunks_exact(3); + | ^^^^^^^^^^^^^^^ + = note: you can access the chunks using `it.0.iter()`, and the remainder using `it.1` + +error: using `chunks_exact_mut` with a constant chunk size + --> tests/ui/chunks_exact_to_as_chunks.rs:31:22 + | +LL | let mut it = arr.chunks_exact_mut(4); + | ^^^^^^^^^^^^^^^^^^^ + | +help: consider using `as_chunks_mut::<4>()` instead + --> tests/ui/chunks_exact_to_as_chunks.rs:31:22 + | +LL | let mut it = arr.chunks_exact_mut(4); + | ^^^^^^^^^^^^^^^^^^^ + = note: you can access the chunks using `it.0.iter()`, and the remainder using `it.1` + +error: aborting due to 4 previous errors + diff --git a/src/tools/clippy/tests/ui/clear_with_drain.fixed b/src/tools/clippy/tests/ui/clear_with_drain.fixed index aaaec700cc9d2..5f8441c0470dc 100644 --- a/src/tools/clippy/tests/ui/clear_with_drain.fixed +++ b/src/tools/clippy/tests/ui/clear_with_drain.fixed @@ -20,11 +20,6 @@ fn vec_range() { let mut v = vec![1, 2, 3]; v.clear(); //~^ clear_with_drain - - // Do lint - let mut v = vec![1, 2, 3]; - v.clear(); - //~^ clear_with_drain } fn vec_range_from() { @@ -45,11 +40,6 @@ fn vec_range_from() { let mut v = vec![1, 2, 3]; v.clear(); //~^ clear_with_drain - - // Do lint - let mut v = vec![1, 2, 3]; - v.clear(); - //~^ clear_with_drain } fn vec_range_full() { @@ -124,11 +114,6 @@ fn vec_deque_range() { let mut deque = VecDeque::from([1, 2, 3]); deque.clear(); //~^ clear_with_drain - - // Do lint - let mut deque = VecDeque::from([1, 2, 3]); - deque.clear(); - //~^ clear_with_drain } fn vec_deque_range_from() { @@ -149,11 +134,6 @@ fn vec_deque_range_from() { let mut deque = VecDeque::from([1, 2, 3]); deque.clear(); //~^ clear_with_drain - - // Do lint - let mut deque = VecDeque::from([1, 2, 3]); - deque.clear(); - //~^ clear_with_drain } fn vec_deque_range_full() { @@ -228,11 +208,6 @@ fn string_range() { let mut s = String::from("Hello, world!"); s.clear(); //~^ clear_with_drain - - // Do lint - let mut s = String::from("Hello, world!"); - s.clear(); - //~^ clear_with_drain } fn string_range_from() { @@ -253,11 +228,6 @@ fn string_range_from() { let mut s = String::from("Hello, world!"); s.clear(); //~^ clear_with_drain - - // Do lint - let mut s = String::from("Hello, world!"); - s.clear(); - //~^ clear_with_drain } fn string_range_full() { diff --git a/src/tools/clippy/tests/ui/clear_with_drain.rs b/src/tools/clippy/tests/ui/clear_with_drain.rs index e36384e09c693..59024dcfe93d8 100644 --- a/src/tools/clippy/tests/ui/clear_with_drain.rs +++ b/src/tools/clippy/tests/ui/clear_with_drain.rs @@ -20,11 +20,6 @@ fn vec_range() { let mut v = vec![1, 2, 3]; v.drain(0..v.len()); //~^ clear_with_drain - - // Do lint - let mut v = vec![1, 2, 3]; - v.drain(usize::MIN..v.len()); - //~^ clear_with_drain } fn vec_range_from() { @@ -45,11 +40,6 @@ fn vec_range_from() { let mut v = vec![1, 2, 3]; v.drain(0..); //~^ clear_with_drain - - // Do lint - let mut v = vec![1, 2, 3]; - v.drain(usize::MIN..); - //~^ clear_with_drain } fn vec_range_full() { @@ -124,11 +114,6 @@ fn vec_deque_range() { let mut deque = VecDeque::from([1, 2, 3]); deque.drain(0..deque.len()); //~^ clear_with_drain - - // Do lint - let mut deque = VecDeque::from([1, 2, 3]); - deque.drain(usize::MIN..deque.len()); - //~^ clear_with_drain } fn vec_deque_range_from() { @@ -149,11 +134,6 @@ fn vec_deque_range_from() { let mut deque = VecDeque::from([1, 2, 3]); deque.drain(0..); //~^ clear_with_drain - - // Do lint - let mut deque = VecDeque::from([1, 2, 3]); - deque.drain(usize::MIN..); - //~^ clear_with_drain } fn vec_deque_range_full() { @@ -228,11 +208,6 @@ fn string_range() { let mut s = String::from("Hello, world!"); s.drain(0..s.len()); //~^ clear_with_drain - - // Do lint - let mut s = String::from("Hello, world!"); - s.drain(usize::MIN..s.len()); - //~^ clear_with_drain } fn string_range_from() { @@ -253,11 +228,6 @@ fn string_range_from() { let mut s = String::from("Hello, world!"); s.drain(0..); //~^ clear_with_drain - - // Do lint - let mut s = String::from("Hello, world!"); - s.drain(usize::MIN..); - //~^ clear_with_drain } fn string_range_full() { diff --git a/src/tools/clippy/tests/ui/clear_with_drain.stderr b/src/tools/clippy/tests/ui/clear_with_drain.stderr index 8b09c26e46af2..b4c71bf60be7f 100644 --- a/src/tools/clippy/tests/ui/clear_with_drain.stderr +++ b/src/tools/clippy/tests/ui/clear_with_drain.stderr @@ -8,124 +8,88 @@ LL | v.drain(0..v.len()); = help: to override `-D warnings` add `#[allow(clippy::clear_with_drain)]` error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:26:7 - | -LL | v.drain(usize::MIN..v.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:46:7 + --> tests/ui/clear_with_drain.rs:41:7 | LL | v.drain(0..); | ^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:51:7 - | -LL | v.drain(usize::MIN..); - | ^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:68:7 + --> tests/ui/clear_with_drain.rs:58:7 | LL | v.drain(..); | ^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `Vec` - --> tests/ui/clear_with_drain.rs:86:7 + --> tests/ui/clear_with_drain.rs:76:7 | LL | v.drain(..v.len()); | ^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:125:11 + --> tests/ui/clear_with_drain.rs:115:11 | LL | deque.drain(0..deque.len()); | ^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:130:11 - | -LL | deque.drain(usize::MIN..deque.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:150:11 + --> tests/ui/clear_with_drain.rs:135:11 | LL | deque.drain(0..); | ^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:155:11 - | -LL | deque.drain(usize::MIN..); - | ^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:172:11 + --> tests/ui/clear_with_drain.rs:152:11 | LL | deque.drain(..); | ^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `VecDeque` - --> tests/ui/clear_with_drain.rs:190:11 + --> tests/ui/clear_with_drain.rs:170:11 | LL | deque.drain(..deque.len()); | ^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:229:7 + --> tests/ui/clear_with_drain.rs:209:7 | LL | s.drain(0..s.len()); | ^^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:234:7 - | -LL | s.drain(usize::MIN..s.len()); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:254:7 + --> tests/ui/clear_with_drain.rs:229:7 | LL | s.drain(0..); | ^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:259:7 - | -LL | s.drain(usize::MIN..); - | ^^^^^^^^^^^^^^^^^^^ help: try: `clear()` - -error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:276:7 + --> tests/ui/clear_with_drain.rs:246:7 | LL | s.drain(..); | ^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `String` - --> tests/ui/clear_with_drain.rs:294:7 + --> tests/ui/clear_with_drain.rs:264:7 | LL | s.drain(..s.len()); | ^^^^^^^^^^^^^^^^ help: try: `clear()` error: `drain` used to clear a `HashSet` - --> tests/ui/clear_with_drain.rs:333:9 + --> tests/ui/clear_with_drain.rs:303:9 | LL | set.drain(); | ^^^^^^^ help: try: `clear()` error: `drain` used to clear a `HashMap` - --> tests/ui/clear_with_drain.rs:353:9 + --> tests/ui/clear_with_drain.rs:323:9 | LL | map.drain(); | ^^^^^^^ help: try: `clear()` error: `drain` used to clear a `BinaryHeap` - --> tests/ui/clear_with_drain.rs:373:10 + --> tests/ui/clear_with_drain.rs:343:10 | LL | heap.drain(); | ^^^^^^^ help: try: `clear()` -error: aborting due to 21 previous errors +error: aborting due to 15 previous errors diff --git a/src/tools/clippy/tests/ui/crashes/ice-16950.rs b/src/tools/clippy/tests/ui/crashes/ice-16950.rs new file mode 100644 index 0000000000000..414b34ebed348 --- /dev/null +++ b/src/tools/clippy/tests/ui/crashes/ice-16950.rs @@ -0,0 +1,27 @@ +//@check-pass +#![feature(trivial_bounds)] + +struct Helper(T); + +trait Unsized { + const SIZE: usize = usize::MAX; +} + +impl Unsized for T {} + +impl Helper { + const SIZE: usize = size_of::(); +} + +struct TrickClippy(str); + +impl TrickClippy { + fn trick_clippy() -> bool + where + Self: Sized, + { + Helper::::SIZE == str::SIZE + } +} + +fn main() {} diff --git a/src/tools/clippy/tests/ui/drain_collect.stderr b/src/tools/clippy/tests/ui/drain_collect.stderr index 46da4e799d2e8..7c1008aebe58a 100644 --- a/src/tools/clippy/tests/ui/drain_collect.stderr +++ b/src/tools/clippy/tests/ui/drain_collect.stderr @@ -1,8 +1,8 @@ -error: you seem to be trying to move all elements into a new `BinaryHeap` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:6:5 | LL | b.drain().collect() - | ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` | note: the lint level is defined here --> tests/ui/drain_collect.rs:1:9 @@ -10,59 +10,59 @@ note: the lint level is defined here LL | #![deny(clippy::drain_collect)] | ^^^^^^^^^^^^^^^^^^^^^ -error: you seem to be trying to move all elements into a new `HashMap` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:15:5 | LL | b.drain().collect() - | ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `HashSet` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:24:5 | LL | b.drain().collect() - | ^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:33:5 | LL | b.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:42:5 | LL | b.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:47:5 | LL | b.drain(0..).collect() - | ^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:52:5 | LL | b.drain(..b.len()).collect() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:57:5 | LL | b.drain(0..b.len()).collect() - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:63:5 | LL | b.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(&mut b)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(&mut b)` -error: you seem to be trying to move all elements into a new `String` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect.rs:72:5 | LL | b.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `std::mem::take(b)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `std::mem::take(b)` error: aborting due to 10 previous errors diff --git a/src/tools/clippy/tests/ui/drain_collect_nostd.stderr b/src/tools/clippy/tests/ui/drain_collect_nostd.stderr index 91b38932fee79..77fcae84d570e 100644 --- a/src/tools/clippy/tests/ui/drain_collect_nostd.stderr +++ b/src/tools/clippy/tests/ui/drain_collect_nostd.stderr @@ -1,8 +1,8 @@ -error: you seem to be trying to move all elements into a new `Vec` +error: draining all elements of a collection into a new collection of the same type --> tests/ui/drain_collect_nostd.rs:7:5 | LL | v.drain(..).collect() - | ^^^^^^^^^^^^^^^^^^^^^ help: consider using `mem::take`: `core::mem::take(v)` + | ^^^^^^^^^^^^^^^^^^^^^ help: use `mem::take` to avoid creating a new allocation: `core::mem::take(v)` | = note: `-D clippy::drain-collect` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::drain_collect)]` diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs index 2e062208935a2..fa2ebddfde3f2 100644 --- a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs +++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs @@ -211,4 +211,18 @@ mod proc_macro_generated { } } +mod issue17255 { + + trait AnotherSimpleTrait<'a> {} + + macro_rules! mac { + ($lt:lifetime, $t:ident, $tr:path) => { + impl<$t: for<'lt> $tr> AnotherSimpleTrait<'_> for $t {} + }; + } + + // Do not lint code expanded from macros + mac!('a, T, super::SimplerTrait); +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs index 87ce517a0a451..6a20967b7fc50 100644 --- a/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs +++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.rs @@ -1,3 +1,5 @@ +//@no-rustfix + #![warn(clippy::extra_unused_type_parameters)] fn unused_where_clause(x: U) @@ -24,4 +26,17 @@ where unimplemented!(); } +// The fix just removes the type parameter from the definition of `unused_ty`, but it doesn't adjust +// its callsites, leading to compilation errors. +mod issue15884 { + fn unused_ty(x: u8) { + //~^ extra_unused_type_parameters + unimplemented!() + } + + fn main() { + unused_ty::(0); + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr index 0765c41609368..2c7051886bf45 100644 --- a/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr +++ b/src/tools/clippy/tests/ui/extra_unused_type_parameters_unfixable.stderr @@ -1,5 +1,5 @@ error: type parameter `T` goes unused in function definition - --> tests/ui/extra_unused_type_parameters_unfixable.rs:3:24 + --> tests/ui/extra_unused_type_parameters_unfixable.rs:5:24 | LL | fn unused_where_clause(x: U) | ^ @@ -9,7 +9,7 @@ LL | fn unused_where_clause(x: U) = help: to override `-D warnings` add `#[allow(clippy::extra_unused_type_parameters)]` error: type parameters go unused in function definition: T, V - --> tests/ui/extra_unused_type_parameters_unfixable.rs:11:30 + --> tests/ui/extra_unused_type_parameters_unfixable.rs:13:30 | LL | fn unused_multi_where_clause(x: U) | ^ ^^^^^^^^^^ @@ -17,12 +17,24 @@ LL | fn unused_multi_where_clause(x: U) = help: consider removing the parameters error: type parameters go unused in function definition: T, U, V - --> tests/ui/extra_unused_type_parameters_unfixable.rs:19:28 + --> tests/ui/extra_unused_type_parameters_unfixable.rs:21:28 | LL | fn unused_all_where_clause() | ^ ^^^^^^^^^^ ^^^^^^^^^^ | = help: consider removing the parameters -error: aborting due to 3 previous errors +error: type parameter `T` goes unused in function definition + --> tests/ui/extra_unused_type_parameters_unfixable.rs:32:17 + | +LL | fn unused_ty(x: u8) { + | ^^^ + | +help: consider removing the parameter + | +LL - fn unused_ty(x: u8) { +LL + fn unused_ty(x: u8) { + | + +error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/filter_next.fixed b/src/tools/clippy/tests/ui/filter_next.fixed new file mode 100644 index 0000000000000..5502fa1fb2733 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_next.fixed @@ -0,0 +1,63 @@ +//@aux-build:option_helpers.rs +#![warn(clippy::filter_next)] +#![expect(clippy::disallowed_names)] +#![allow(clippy::useless_vec)] + +extern crate option_helpers; + +use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives}; + +fn main() {} + +fn filter_next() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Single-line case. + let _ = v.iter().find(|&x| *x < 0); + //~^ filter_next + + let _ = v.iter().rfind(|&x| *x < 0); + //~^ filter_next + + // Multi-line case. + #[rustfmt::skip] + let _ = v.iter().find(|&x| { + //~^ filter_next + *x < 0 + }); + + #[rustfmt::skip] + let _ = v.iter().rfind(|&x| { + //~^ filter_next + *x < 0 + }); + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next(); +} + +fn filter_next_back() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next_back(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next_back(); +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = vec![1].into_iter().rfind(|&x| x < 0); + //~^ filter_next +} + +#[clippy::msrv = "1.26"] +fn msrv_1_26() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); +} diff --git a/src/tools/clippy/tests/ui/filter_next.rs b/src/tools/clippy/tests/ui/filter_next.rs new file mode 100644 index 0000000000000..c8e9f060cd609 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_next.rs @@ -0,0 +1,65 @@ +//@aux-build:option_helpers.rs +#![warn(clippy::filter_next)] +#![expect(clippy::disallowed_names)] +#![allow(clippy::useless_vec)] + +extern crate option_helpers; + +use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives}; + +fn main() {} + +fn filter_next() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Single-line case. + let _ = v.iter().filter(|&x| *x < 0).next(); + //~^ filter_next + + let _ = v.iter().filter(|&x| *x < 0).next_back(); + //~^ filter_next + + // Multi-line case. + #[rustfmt::skip] + let _ = v.iter().filter(|&x| { + //~^ filter_next + *x < 0 + } + ).next(); + + #[rustfmt::skip] + let _ = v.iter().filter(|&x| { + //~^ filter_next + *x < 0 + } + ).next_back(); + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next(); +} + +fn filter_next_back() { + let v = [3, 2, 1, 0, -1, -2, -3]; + + // Check that we don't lint if the caller is not an `Iterator`. + let foo = IteratorFalsePositives { foo: 0 }; + let _ = foo.filter().next_back(); + + let foo = IteratorMethodFalsePositives {}; + let _ = foo.filter(42).next_back(); +} + +#[clippy::msrv = "1.27"] +fn msrv_1_27() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); + //~^ filter_next +} + +#[clippy::msrv = "1.26"] +fn msrv_1_26() { + let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); +} diff --git a/src/tools/clippy/tests/ui/filter_next.stderr b/src/tools/clippy/tests/ui/filter_next.stderr new file mode 100644 index 0000000000000..19c2428c3d17d --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_next.stderr @@ -0,0 +1,78 @@ +error: called `filter(..).next()` on an `Iterator` + --> tests/ui/filter_next.rs:16:13 + | +LL | let _ = v.iter().filter(|&x| *x < 0).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::filter-next` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` +help: use `.find(..)` instead + | +LL - let _ = v.iter().filter(|&x| *x < 0).next(); +LL + let _ = v.iter().find(|&x| *x < 0); + | + +error: called `filter(..).next_back()` on an `DoubleEndedIterator` + --> tests/ui/filter_next.rs:19:13 + | +LL | let _ = v.iter().filter(|&x| *x < 0).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `.rfind(..)` instead + | +LL - let _ = v.iter().filter(|&x| *x < 0).next_back(); +LL + let _ = v.iter().rfind(|&x| *x < 0); + | + +error: called `filter(..).next()` on an `Iterator` + --> tests/ui/filter_next.rs:24:13 + | +LL | let _ = v.iter().filter(|&x| { + | _____________^ +LL | | +LL | | *x < 0 +LL | | } +LL | | ).next(); + | |___________________________^ + | +help: use `.find(..)` instead + | +LL ~ let _ = v.iter().find(|&x| { +LL + +LL + *x < 0 +LL ~ }); + | + +error: called `filter(..).next_back()` on an `DoubleEndedIterator` + --> tests/ui/filter_next.rs:31:13 + | +LL | let _ = v.iter().filter(|&x| { + | _____________^ +LL | | +LL | | *x < 0 +LL | | } +LL | | ).next_back(); + | |________________________________^ + | +help: use `.rfind(..)` instead + | +LL ~ let _ = v.iter().rfind(|&x| { +LL + +LL + *x < 0 +LL ~ }); + | + +error: called `filter(..).next_back()` on an `DoubleEndedIterator` + --> tests/ui/filter_next.rs:58:13 + | +LL | let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `.rfind(..)` instead + | +LL - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); +LL + let _ = vec![1].into_iter().rfind(|&x| x < 0); + | + +error: aborting due to 5 previous errors + diff --git a/src/tools/clippy/tests/ui/filter_next_unfixable.rs b/src/tools/clippy/tests/ui/filter_next_unfixable.rs new file mode 100644 index 0000000000000..07a0db1353500 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_next_unfixable.rs @@ -0,0 +1,19 @@ +//@no-rustfix +#![warn(clippy::filter_next)] + +fn main() {} + +// The fixed version doesn't compile, as `iter` isn't `mut`. +// We do emit a note suggesting adding it, but not an autofix +pub fn issue10029() { + { + let iter = (0..10); + let _ = iter.filter(|_| true).next(); + //~^ filter_next + } + { + let iter = (0..10); + let _ = iter.filter(|_| true).next_back(); + //~^ filter_next + } +} diff --git a/src/tools/clippy/tests/ui/filter_next_unfixable.stderr b/src/tools/clippy/tests/ui/filter_next_unfixable.stderr new file mode 100644 index 0000000000000..2c35badec4853 --- /dev/null +++ b/src/tools/clippy/tests/ui/filter_next_unfixable.stderr @@ -0,0 +1,38 @@ +error: called `filter(..).next()` on an `Iterator` + --> tests/ui/filter_next_unfixable.rs:11:17 + | +LL | let _ = iter.filter(|_| true).next(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: you will also need to make `iter` mutable, because `find` takes `&mut self` + --> tests/ui/filter_next_unfixable.rs:10:13 + | +LL | let iter = (0..10); + | ^^^^ + = note: `-D clippy::filter-next` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` +help: use `.find(..)` instead + | +LL - let _ = iter.filter(|_| true).next(); +LL + let _ = iter.find(|_| true); + | + +error: called `filter(..).next_back()` on an `DoubleEndedIterator` + --> tests/ui/filter_next_unfixable.rs:16:17 + | +LL | let _ = iter.filter(|_| true).next_back(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: you will also need to make `iter` mutable, because `rfind` takes `&mut self` + --> tests/ui/filter_next_unfixable.rs:15:13 + | +LL | let iter = (0..10); + | ^^^^ +help: use `.rfind(..)` instead + | +LL - let _ = iter.filter(|_| true).next_back(); +LL + let _ = iter.rfind(|_| true); + | + +error: aborting due to 2 previous errors + diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr index 3f2ca76826f45..b14d0585f2c96 100644 --- a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr +++ b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr @@ -1,121 +1,122 @@ -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:19:13 | LL | let _ = a * b + c; | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)` | + = note: the performance gain from `mul_add` may vary depending on the target architecture = note: `-D clippy::suboptimal-flops` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::suboptimal_flops)]` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:21:13 | LL | let _ = a * b - c; | ^^^^^^^^^ help: consider using: `a.mul_add(b, -c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:23:13 | LL | let _ = c + a * b; | ^^^^^^^^^ help: consider using: `a.mul_add(b, c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:25:13 | LL | let _ = c - a * b; | ^^^^^^^^^ help: consider using: `a.mul_add(-b, c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:27:13 | LL | let _ = a + 2.0 * 4.0; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, a)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:29:13 | LL | let _ = a + 2. * 4.; | ^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4., a)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:32:13 | LL | let _ = (a * b) + c; | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:34:13 | LL | let _ = c + (a * b); | ^^^^^^^^^^^ help: consider using: `a.mul_add(b, c)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:36:13 | LL | let _ = a * b * c + d; | ^^^^^^^^^^^^^ help: consider using: `(a * b).mul_add(c, d)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:39:13 | LL | let _ = a.mul_add(b, c) * a.mul_add(b, c) + a.mul_add(b, c) + c; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `a.mul_add(b, c).mul_add(a.mul_add(b, c), a.mul_add(b, c))` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:41:13 | LL | let _ = 1234.567_f64 * 45.67834_f64 + 0.0004_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `1234.567_f64.mul_add(45.67834_f64, 0.0004_f64)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:44:13 | LL | let _ = (a * a + b).sqrt(); | ^^^^^^^^^^^ help: consider using: `a.mul_add(a, b)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:48:13 | LL | let _ = a - (b * u as f64); | ^^^^^^^^^^^^^^^^^^ help: consider using: `b.mul_add(-(u as f64), a)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:102:13 | LL | let _ = 0.5 + 2.0 * x; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:104:13 | LL | let _ = 2.0 * x + 0.5; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:107:13 | LL | let _ = x + 2.0 * 4.0; | ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, x)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:111:13 | LL | let _ = y * 2.0 + 0.5; | ^^^^^^^^^^^^^ help: consider using: `y.mul_add(2.0, 0.5)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:113:13 | LL | let _ = 1.0 * 2.0 + 0.5; | ^^^^^^^^^^^^^^^ help: consider using: `1.0f64.mul_add(2.0, 0.5)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:122:5 | LL | a += b * c; | ^^^^^^^^^^ help: consider using: `a = b.mul_add(c, a)` -error: multiply and add expressions can be calculated more efficiently and accurately +error: multiply and add expressions may be calculated more efficiently and accurately --> tests/ui/floating_point_mul_add.rs:125:5 | LL | a -= b * c; diff --git a/src/tools/clippy/tests/ui/let_underscore_future.rs b/src/tools/clippy/tests/ui/let_underscore_future.rs index eb1a985a91172..17c73876302dd 100644 --- a/src/tools/clippy/tests/ui/let_underscore_future.rs +++ b/src/tools/clippy/tests/ui/let_underscore_future.rs @@ -1,4 +1,5 @@ use std::future::Future; +use std::pin::Pin; async fn some_async_fn() {} @@ -10,6 +11,10 @@ fn custom() -> impl Future { fn do_something_to_future(future: &mut impl Future) {} +fn boxed() -> Pin>> { + Box::pin(async {}) +} + fn main() { let _ = some_async_fn(); //~^ let_underscore_future @@ -21,4 +26,7 @@ fn main() { do_something_to_future(&mut future); let _ = future; //~^ let_underscore_future + + // Typed bindings are an intentional discard, see also `let_underscore_untyped`. + let _: Pin>> = boxed(); } diff --git a/src/tools/clippy/tests/ui/let_underscore_future.stderr b/src/tools/clippy/tests/ui/let_underscore_future.stderr index baa489551d4a3..12cbfff133e9c 100644 --- a/src/tools/clippy/tests/ui/let_underscore_future.stderr +++ b/src/tools/clippy/tests/ui/let_underscore_future.stderr @@ -1,5 +1,5 @@ error: non-binding `let` on a future - --> tests/ui/let_underscore_future.rs:14:5 + --> tests/ui/let_underscore_future.rs:19:5 | LL | let _ = some_async_fn(); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | let _ = some_async_fn(); = help: to override `-D warnings` add `#[allow(clippy::let_underscore_future)]` error: non-binding `let` on a future - --> tests/ui/let_underscore_future.rs:17:5 + --> tests/ui/let_underscore_future.rs:22:5 | LL | let _ = custom(); | ^^^^^^^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | let _ = custom(); = help: consider awaiting the future or dropping explicitly with `std::mem::drop` error: non-binding `let` on a future - --> tests/ui/let_underscore_future.rs:22:5 + --> tests/ui/let_underscore_future.rs:27:5 | LL | let _ = future; | ^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/manual_abs_diff.fixed b/src/tools/clippy/tests/ui/manual_abs_diff.fixed index 2766942140ce4..3b5cac5dfc986 100644 --- a/src/tools/clippy/tests/ui/manual_abs_diff.fixed +++ b/src/tools/clippy/tests/ui/manual_abs_diff.fixed @@ -51,6 +51,7 @@ fn main() { } // FIXME: bunch of patterns that should be linted +#[expect(clippy::needless_late_init)] fn fixme() { let a: usize = 5; let b: usize = 3; diff --git a/src/tools/clippy/tests/ui/manual_abs_diff.rs b/src/tools/clippy/tests/ui/manual_abs_diff.rs index 2c408f2be3754..f845dd9b149a9 100644 --- a/src/tools/clippy/tests/ui/manual_abs_diff.rs +++ b/src/tools/clippy/tests/ui/manual_abs_diff.rs @@ -61,6 +61,7 @@ fn main() { } // FIXME: bunch of patterns that should be linted +#[expect(clippy::needless_late_init)] fn fixme() { let a: usize = 5; let b: usize = 3; diff --git a/src/tools/clippy/tests/ui/manual_abs_diff.stderr b/src/tools/clippy/tests/ui/manual_abs_diff.stderr index bb6d312b435f8..b441ac9751f05 100644 --- a/src/tools/clippy/tests/ui/manual_abs_diff.stderr +++ b/src/tools/clippy/tests/ui/manual_abs_diff.stderr @@ -80,7 +80,7 @@ LL | let _ = if a > b { (a - b) as u32 } else { (b - a) as u32 }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with `abs_diff`: `a.abs_diff(b)` error: manual absolute difference pattern without using `abs_diff` - --> tests/ui/manual_abs_diff.rs:119:5 + --> tests/ui/manual_abs_diff.rs:120:5 | LL | / if a < b { LL | | diff --git a/src/tools/clippy/tests/ui/manual_option_zip.fixed b/src/tools/clippy/tests/ui/manual_option_zip.fixed index 0a903fb784075..8b821f8737cbb 100644 --- a/src/tools/clippy/tests/ui/manual_option_zip.fixed +++ b/src/tools/clippy/tests/ui/manual_option_zip.fixed @@ -21,11 +21,6 @@ fn should_lint() { let _ = None::.zip(b); //~^ manual_option_zip - // with function call as map receiver - let a: Option = Some(1); - let _ = a.zip(get_option()); - //~^ manual_option_zip - // tuple order reversed: (inner, outer) instead of (outer, inner) let a: Option = Some(1); let b: Option = Some(2); @@ -123,3 +118,26 @@ fn issue16968() { let opts = [1, 2]; let _ = a.and_then(|a| opts.into_iter().find(|b| *b == a).map(|b| (a, b))); } + +fn issue17253() { + // don't trigger the lint if map receiver is a lazy evaluated expression + // because `a.zip(b_func())` requires eager evaluation of the argument, + // preventing the otherwise conditional execution of `b_func()` + + use std::hint::black_box; + let a: Option = Some(1); + + // conditional function call + let _ = a.and_then(|a| black_box(get_option()).map(|b| (a, b))); + + let mut b = 2; + + // conditional side effects + let _ = a.and_then(|a| { + { + b /= 2; + Some(b) + } + .map(|b| (a, b)) + }); +} diff --git a/src/tools/clippy/tests/ui/manual_option_zip.rs b/src/tools/clippy/tests/ui/manual_option_zip.rs index 942d8aea3e835..5d059a98ee12c 100644 --- a/src/tools/clippy/tests/ui/manual_option_zip.rs +++ b/src/tools/clippy/tests/ui/manual_option_zip.rs @@ -21,11 +21,6 @@ fn should_lint() { let _ = None::.and_then(|a| b.map(|b| (a, b))); //~^ manual_option_zip - // with function call as map receiver - let a: Option = Some(1); - let _ = a.and_then(|a| get_option().map(|b| (a, b))); - //~^ manual_option_zip - // tuple order reversed: (inner, outer) instead of (outer, inner) let a: Option = Some(1); let b: Option = Some(2); @@ -123,3 +118,26 @@ fn issue16968() { let opts = [1, 2]; let _ = a.and_then(|a| opts.into_iter().find(|b| *b == a).map(|b| (a, b))); } + +fn issue17253() { + // don't trigger the lint if map receiver is a lazy evaluated expression + // because `a.zip(b_func())` requires eager evaluation of the argument, + // preventing the otherwise conditional execution of `b_func()` + + use std::hint::black_box; + let a: Option = Some(1); + + // conditional function call + let _ = a.and_then(|a| black_box(get_option()).map(|b| (a, b))); + + let mut b = 2; + + // conditional side effects + let _ = a.and_then(|a| { + { + b /= 2; + Some(b) + } + .map(|b| (a, b)) + }); +} diff --git a/src/tools/clippy/tests/ui/manual_option_zip.stderr b/src/tools/clippy/tests/ui/manual_option_zip.stderr index 473f21702654c..55175660b7a26 100644 --- a/src/tools/clippy/tests/ui/manual_option_zip.stderr +++ b/src/tools/clippy/tests/ui/manual_option_zip.stderr @@ -20,34 +20,28 @@ LL | let _ = None::.and_then(|a| b.map(|b| (a, b))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `None::.zip(b)` error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:26:13 - | -LL | let _ = a.and_then(|a| get_option().map(|b| (a, b))); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(get_option())` - -error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:32:13 + --> tests/ui/manual_option_zip.rs:27:13 | LL | let _ = a.and_then(|a| b.map(|b| (b, a))); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.zip(a)` error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:39:13 + --> tests/ui/manual_option_zip.rs:34:13 | LL | let _ = a.and_then(|a| { b.map(|b| (a, b)) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)` error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:42:13 + --> tests/ui/manual_option_zip.rs:37:13 | LL | let _ = a.and_then(|a| b.map(|b| { (a, b) })); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)` error: manual implementation of `Option::zip` - --> tests/ui/manual_option_zip.rs:45:13 + --> tests/ui/manual_option_zip.rs:40:13 | LL | let _ = a.and_then(|a| { b.map(|b| { (a, b) }) }); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `a.zip(b)` -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.fixed b/src/tools/clippy/tests/ui/manual_slice_fill.fixed index 38ab10b539c18..aa8037af771c2 100644 --- a/src/tools/clippy/tests/ui/manual_slice_fill.fixed +++ b/src/tools/clippy/tests/ui/manual_slice_fill.fixed @@ -34,6 +34,38 @@ fn should_lint() { some_slice.fill(0); } +fn should_lint_direct_mutref_array(s: &mut [u8; 1]) { + s.fill(0); +} + +fn should_lint_direct_mutref_array_non_zero(s: &mut [u8; 4]) { + s.fill(42); +} + +fn should_lint_direct_mutref_array_variable(s: &mut [i32; 3]) { + let x = 7; + s.fill(x); +} + +fn should_not_lint_direct_mutref_array_fn(s: &mut [usize; 2]) { + for slot in s { + *slot = num(); + } +} + +fn should_not_lint_direct_mutref_array_iter_used(s: &mut [u8; 3]) { + for slot in s { + *slot = !*slot; + } +} + +fn should_not_lint_direct_mutref_array_extra_stmt(s: &mut [u8; 2]) { + for slot in s { + *slot = 0; + println!("foo"); + } +} + fn should_not_lint() { let mut some_slice = [1, 2, 3, 4, 5]; diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.rs b/src/tools/clippy/tests/ui/manual_slice_fill.rs index d80b23ce985d1..6ca200a046295 100644 --- a/src/tools/clippy/tests/ui/manual_slice_fill.rs +++ b/src/tools/clippy/tests/ui/manual_slice_fill.rs @@ -47,6 +47,47 @@ fn should_lint() { } } +fn should_lint_direct_mutref_array(s: &mut [u8; 1]) { + for slot in s { + //~^ manual_slice_fill + *slot = 0; + } +} + +fn should_lint_direct_mutref_array_non_zero(s: &mut [u8; 4]) { + for slot in s { + //~^ manual_slice_fill + *slot = 42; + } +} + +fn should_lint_direct_mutref_array_variable(s: &mut [i32; 3]) { + let x = 7; + for slot in s { + //~^ manual_slice_fill + *slot = x; + } +} + +fn should_not_lint_direct_mutref_array_fn(s: &mut [usize; 2]) { + for slot in s { + *slot = num(); + } +} + +fn should_not_lint_direct_mutref_array_iter_used(s: &mut [u8; 3]) { + for slot in s { + *slot = !*slot; + } +} + +fn should_not_lint_direct_mutref_array_extra_stmt(s: &mut [u8; 2]) { + for slot in s { + *slot = 0; + println!("foo"); + } +} + fn should_not_lint() { let mut some_slice = [1, 2, 3, 4, 5]; diff --git a/src/tools/clippy/tests/ui/manual_slice_fill.stderr b/src/tools/clippy/tests/ui/manual_slice_fill.stderr index 38e43d5b4e06e..cd1f36f4291f8 100644 --- a/src/tools/clippy/tests/ui/manual_slice_fill.stderr +++ b/src/tools/clippy/tests/ui/manual_slice_fill.stderr @@ -38,5 +38,32 @@ LL | | // foo LL | | } | |_____^ help: try: `some_slice.fill(0);` -error: aborting due to 4 previous errors +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:51:5 + | +LL | / for slot in s { +LL | | +LL | | *slot = 0; +LL | | } + | |_____^ help: try: `s.fill(0);` + +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:58:5 + | +LL | / for slot in s { +LL | | +LL | | *slot = 42; +LL | | } + | |_____^ help: try: `s.fill(42);` + +error: manually filling a slice + --> tests/ui/manual_slice_fill.rs:66:5 + | +LL | / for slot in s { +LL | | +LL | | *slot = x; +LL | | } + | |_____^ help: try: `s.fill(x);` + +error: aborting due to 7 previous errors diff --git a/src/tools/clippy/tests/ui/map_unwrap_or.rs b/src/tools/clippy/tests/ui/map_unwrap_or.rs index bcbb14d5c2e1b..cfad8efc23979 100644 --- a/src/tools/clippy/tests/ui/map_unwrap_or.rs +++ b/src/tools/clippy/tests/ui/map_unwrap_or.rs @@ -161,3 +161,10 @@ fn issue15752() { x.map(|y| y.0).unwrap_or(&[]); //~^ map_unwrap_or } + +fn issue16901() { + let raw = String::from("scope:value"); + let after_scope = raw.split_once(':').map(|(_, v)| v).unwrap_or(&raw); + //~^ map_unwrap_or + let _: &str = after_scope; +} diff --git a/src/tools/clippy/tests/ui/map_unwrap_or.stderr b/src/tools/clippy/tests/ui/map_unwrap_or.stderr index a90da4a97e0c8..cd62c17848583 100644 --- a/src/tools/clippy/tests/ui/map_unwrap_or.stderr +++ b/src/tools/clippy/tests/ui/map_unwrap_or.stderr @@ -238,5 +238,11 @@ error: called `map().unwrap_or()` on an `Option` value LL | x.map(|y| y.0).unwrap_or(&[]); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: aborting due to 16 previous errors +error: called `map().unwrap_or()` on an `Option` value + --> tests/ui/map_unwrap_or.rs:167:23 + | +LL | let after_scope = raw.split_once(':').map(|(_, v)| v).unwrap_or(&raw); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/methods.rs b/src/tools/clippy/tests/ui/methods.rs index f73ec563dceb1..10f0934bede74 100644 --- a/src/tools/clippy/tests/ui/methods.rs +++ b/src/tools/clippy/tests/ui/methods.rs @@ -1,36 +1,10 @@ -//@aux-build:option_helpers.rs - -#![allow( - clippy::disallowed_names, - clippy::default_trait_access, - clippy::let_underscore_untyped, - clippy::missing_docs_in_private_items, - clippy::missing_safety_doc, - clippy::non_ascii_literal, - clippy::new_without_default, - clippy::needless_pass_by_value, - clippy::needless_lifetimes, - clippy::elidable_lifetime_names, - clippy::print_stdout, - clippy::must_use_candidate, - clippy::use_self, - clippy::useless_format, - clippy::wrong_self_convention, - clippy::unused_async, - clippy::unused_self, - clippy::useless_vec -)] - -#[macro_use] -extern crate option_helpers; +#![warn(clippy::filter_next, clippy::new_ret_no_self)] use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; use std::ops::Mul; use std::rc::{self, Rc}; use std::sync::{self, Arc}; -use option_helpers::{IteratorFalsePositives, IteratorMethodFalsePositives}; - struct Lt<'a> { foo: &'a u32, } @@ -115,43 +89,4 @@ impl Mul for T { } } -/// Checks implementation of `FILTER_NEXT` lint. -#[rustfmt::skip] -fn filter_next() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - - // Multi-line case. - let _ = v.iter().filter(|&x| { - //~^ filter_next - *x < 0 - } - ).next(); - - // Check that we don't lint if the caller is not an `Iterator`. - let foo = IteratorFalsePositives { foo: 0 }; - let _ = foo.filter().next(); - - let foo = IteratorMethodFalsePositives {}; - let _ = foo.filter(42).next(); -} - -#[rustfmt::skip] -fn filter_next_back() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - - // Multi-line case. - let _ = v.iter().filter(|&x| { - //~^ filter_next - *x < 0 - } - ).next_back(); - - // Check that we don't lint if the caller is not an `Iterator`. - let foo = IteratorFalsePositives { foo: 0 }; - let _ = foo.filter().next_back(); - - let foo = IteratorMethodFalsePositives {}; - let _ = foo.filter(42).next_back(); -} - fn main() {} diff --git a/src/tools/clippy/tests/ui/methods.stderr b/src/tools/clippy/tests/ui/methods.stderr index 45efea4ee01cd..1136b33ea9104 100644 --- a/src/tools/clippy/tests/ui/methods.stderr +++ b/src/tools/clippy/tests/ui/methods.stderr @@ -1,5 +1,5 @@ error: methods called `new` usually return `Self` - --> tests/ui/methods.rs:102:5 + --> tests/ui/methods.rs:76:5 | LL | / fn new() -> i32 { LL | | @@ -10,30 +10,5 @@ LL | | } = note: `-D clippy::new-ret-no-self` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::new_ret_no_self)]` -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> tests/ui/methods.rs:124:13 - | -LL | let _ = v.iter().filter(|&x| { - | _____________^ -LL | | -LL | | *x < 0 -LL | | } -LL | | ).next(); - | |___________________________^ - | - = note: `-D clippy::filter-next` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` - -error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead - --> tests/ui/methods.rs:143:13 - | -LL | let _ = v.iter().filter(|&x| { - | _____________^ -LL | | -LL | | *x < 0 -LL | | } -LL | | ).next_back(); - | |________________________________^ - -error: aborting due to 3 previous errors +error: aborting due to 1 previous error diff --git a/src/tools/clippy/tests/ui/methods_fixable.fixed b/src/tools/clippy/tests/ui/methods_fixable.fixed deleted file mode 100644 index 287d8d881ec28..0000000000000 --- a/src/tools/clippy/tests/ui/methods_fixable.fixed +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::filter_next)] -#![allow(clippy::useless_vec)] - -/// Checks implementation of `FILTER_NEXT` lint. -fn main() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - - // Single-line case. - let _ = v.iter().find(|&x| *x < 0); - //~^ filter_next - - let _ = v.iter().rfind(|&x| *x < 0); - //~^ filter_next -} - -#[clippy::msrv = "1.27"] -fn msrv_1_27() { - let _ = vec![1].into_iter().rfind(|&x| x < 0); - //~^ filter_next -} - -#[clippy::msrv = "1.26"] -fn msrv_1_26() { - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); -} diff --git a/src/tools/clippy/tests/ui/methods_fixable.rs b/src/tools/clippy/tests/ui/methods_fixable.rs deleted file mode 100644 index 11ce1b63560db..0000000000000 --- a/src/tools/clippy/tests/ui/methods_fixable.rs +++ /dev/null @@ -1,25 +0,0 @@ -#![warn(clippy::filter_next)] -#![allow(clippy::useless_vec)] - -/// Checks implementation of `FILTER_NEXT` lint. -fn main() { - let v = vec![3, 2, 1, 0, -1, -2, -3]; - - // Single-line case. - let _ = v.iter().filter(|&x| *x < 0).next(); - //~^ filter_next - - let _ = v.iter().filter(|&x| *x < 0).next_back(); - //~^ filter_next -} - -#[clippy::msrv = "1.27"] -fn msrv_1_27() { - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); - //~^ filter_next -} - -#[clippy::msrv = "1.26"] -fn msrv_1_26() { - let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); -} diff --git a/src/tools/clippy/tests/ui/methods_fixable.stderr b/src/tools/clippy/tests/ui/methods_fixable.stderr deleted file mode 100644 index d26b5e9ac271e..0000000000000 --- a/src/tools/clippy/tests/ui/methods_fixable.stderr +++ /dev/null @@ -1,23 +0,0 @@ -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> tests/ui/methods_fixable.rs:9:13 - | -LL | let _ = v.iter().filter(|&x| *x < 0).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v.iter().find(|&x| *x < 0)` - | - = note: `-D clippy::filter-next` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` - -error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead - --> tests/ui/methods_fixable.rs:12:13 - | -LL | let _ = v.iter().filter(|&x| *x < 0).next_back(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `v.iter().rfind(|&x| *x < 0)` - -error: called `filter(..).next_back()` on an `DoubleEndedIterator`. This is more succinctly expressed by calling `.rfind(..)` instead - --> tests/ui/methods_fixable.rs:18:13 - | -LL | let _ = vec![1].into_iter().filter(|&x| x < 0).next_back(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `vec![1].into_iter().rfind(|&x| x < 0)` - -error: aborting due to 3 previous errors - diff --git a/src/tools/clippy/tests/ui/methods_unfixable.rs b/src/tools/clippy/tests/ui/methods_unfixable.rs deleted file mode 100644 index 46a5d95eb1b72..0000000000000 --- a/src/tools/clippy/tests/ui/methods_unfixable.rs +++ /dev/null @@ -1,9 +0,0 @@ -#![warn(clippy::filter_next)] -//@no-rustfix -fn main() {} - -pub fn issue10029() { - let iter = (0..10); - let _ = iter.filter(|_| true).next(); - //~^ filter_next -} diff --git a/src/tools/clippy/tests/ui/methods_unfixable.stderr b/src/tools/clippy/tests/ui/methods_unfixable.stderr deleted file mode 100644 index 137112ae41795..0000000000000 --- a/src/tools/clippy/tests/ui/methods_unfixable.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead - --> tests/ui/methods_unfixable.rs:7:13 - | -LL | let _ = iter.filter(|_| true).next(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `iter.find(|_| true)` - | -help: you will also need to make `iter` mutable, because `find` takes `&mut self` - --> tests/ui/methods_unfixable.rs:6:9 - | -LL | let iter = (0..10); - | ^^^^ - = note: `-D clippy::filter-next` implied by `-D warnings` - = help: to override `-D warnings` add `#[allow(clippy::filter_next)]` - -error: aborting due to 1 previous error - diff --git a/src/tools/clippy/tests/ui/min_ident_chars.rs b/src/tools/clippy/tests/ui/min_ident_chars.rs index e2f82e2a182f6..298798be7a1e6 100644 --- a/src/tools/clippy/tests/ui/min_ident_chars.rs +++ b/src/tools/clippy/tests/ui/min_ident_chars.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs -#![allow(irrefutable_let_patterns, nonstandard_style, unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::min_ident_chars)] +#![expect(irrefutable_let_patterns, nonstandard_style)] +#![allow(clippy::struct_field_names)] extern crate proc_macros; use proc_macros::{external, with_span}; diff --git a/src/tools/clippy/tests/ui/min_max.rs b/src/tools/clippy/tests/ui/min_max.rs index ee19d3ff71421..cc97e496514bc 100644 --- a/src/tools/clippy/tests/ui/min_max.rs +++ b/src/tools/clippy/tests/ui/min_max.rs @@ -1,4 +1,5 @@ -#![allow(clippy::manual_clamp)] +#![warn(clippy::min_max)] +#![expect(clippy::manual_clamp)] use std::cmp::{max as my_max, max, min as my_min, min}; diff --git a/src/tools/clippy/tests/ui/min_max.stderr b/src/tools/clippy/tests/ui/min_max.stderr index 87510a465a08b..84b4d37545529 100644 --- a/src/tools/clippy/tests/ui/min_max.stderr +++ b/src/tools/clippy/tests/ui/min_max.stderr @@ -1,79 +1,80 @@ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:21:5 + --> tests/ui/min_max.rs:22:5 | LL | min(1, max(3, x)); | ^^^^^^^^^^^^^^^^^ | - = note: `#[deny(clippy::min_max)]` on by default + = note: `-D clippy::min-max` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::min_max)]` error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:24:5 + --> tests/ui/min_max.rs:25:5 | LL | min(max(3, x), 1); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:27:5 + --> tests/ui/min_max.rs:28:5 | LL | max(min(x, 1), 3); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:30:5 + --> tests/ui/min_max.rs:31:5 | LL | max(3, min(x, 1)); | ^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:33:5 + --> tests/ui/min_max.rs:34:5 | LL | my_max(3, my_min(x, 1)); | ^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:44:5 + --> tests/ui/min_max.rs:45:5 | LL | min("Apple", max("Zoo", s)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:47:5 + --> tests/ui/min_max.rs:48:5 | LL | max(min(s, "Apple"), "Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:53:5 + --> tests/ui/min_max.rs:54:5 | LL | x.min(1).max(3); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:56:5 + --> tests/ui/min_max.rs:57:5 | LL | x.max(3).min(1); | ^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:59:5 + --> tests/ui/min_max.rs:60:5 | LL | f.max(3f32).min(1f32); | ^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:66:5 + --> tests/ui/min_max.rs:67:5 | LL | max(x.min(1), 3); | ^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:71:5 + --> tests/ui/min_max.rs:72:5 | LL | s.max("Zoo").min("Apple"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ error: this `min`/`max` combination leads to constant result - --> tests/ui/min_max.rs:74:5 + --> tests/ui/min_max.rs:75:5 | LL | s.min("Apple").max("Zoo"); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_attr.rs index 4627bef28a29b..c624774a8a9cf 100644 --- a/src/tools/clippy/tests/ui/min_rust_version_attr.rs +++ b/src/tools/clippy/tests/ui/min_rust_version_attr.rs @@ -1,4 +1,3 @@ -#![allow(clippy::redundant_clone)] #![feature(custom_inner_attributes)] fn main() {} diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr index 809b1cfe73bf0..842bd5c238618 100644 --- a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr +++ b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr @@ -1,5 +1,5 @@ error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:13:19 + --> tests/ui/min_rust_version_attr.rs:12:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -8,7 +8,7 @@ LL | let log2_10 = 3.321928094887362; = note: `#[deny(clippy::approx_constant)]` on by default error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:19:19 + --> tests/ui/min_rust_version_attr.rs:18:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -16,7 +16,7 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:30:19 + --> tests/ui/min_rust_version_attr.rs:29:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -24,7 +24,7 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:41:19 + --> tests/ui/min_rust_version_attr.rs:40:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -32,7 +32,7 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:52:19 + --> tests/ui/min_rust_version_attr.rs:51:19 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL | let log2_10 = 3.321928094887362; = help: consider using the constant directly error: approximate value of `f{32, 64}::consts::LOG2_10` found - --> tests/ui/min_rust_version_attr.rs:60:27 + --> tests/ui/min_rust_version_attr.rs:59:27 | LL | let log2_10 = 3.321928094887362; | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/mismatching_type_param_order.rs b/src/tools/clippy/tests/ui/mismatching_type_param_order.rs index 11ff865b583bf..49d7031441066 100644 --- a/src/tools/clippy/tests/ui/mismatching_type_param_order.rs +++ b/src/tools/clippy/tests/ui/mismatching_type_param_order.rs @@ -1,5 +1,4 @@ #![warn(clippy::mismatching_type_param_order)] -#![allow(clippy::disallowed_names, clippy::needless_lifetimes)] fn main() { struct Foo { diff --git a/src/tools/clippy/tests/ui/mismatching_type_param_order.stderr b/src/tools/clippy/tests/ui/mismatching_type_param_order.stderr index 214d9d734d1bb..a1b5ca7ec26c7 100644 --- a/src/tools/clippy/tests/ui/mismatching_type_param_order.stderr +++ b/src/tools/clippy/tests/ui/mismatching_type_param_order.stderr @@ -1,5 +1,5 @@ error: `Foo` has a similarly named generic type parameter `B` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:11:20 + --> tests/ui/mismatching_type_param_order.rs:10:20 | LL | impl Foo {} | ^ @@ -9,7 +9,7 @@ LL | impl Foo {} = help: to override `-D warnings` add `#[allow(clippy::mismatching_type_param_order)]` error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:11:23 + --> tests/ui/mismatching_type_param_order.rs:10:23 | LL | impl Foo {} | ^ @@ -17,7 +17,7 @@ LL | impl Foo {} = help: try `B`, or a name that does not conflict with `Foo`'s generic params error: `Foo` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:16:23 + --> tests/ui/mismatching_type_param_order.rs:15:23 | LL | impl Foo {} | ^ @@ -25,7 +25,7 @@ LL | impl Foo {} = help: try `B`, or a name that does not conflict with `Foo`'s generic params error: `FooLifetime` has a similarly named generic type parameter `B` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:28:44 + --> tests/ui/mismatching_type_param_order.rs:27:44 | LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} | ^ @@ -33,7 +33,7 @@ LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} = help: try `A`, or a name that does not conflict with `FooLifetime`'s generic params error: `FooLifetime` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:28:47 + --> tests/ui/mismatching_type_param_order.rs:27:47 | LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} | ^ @@ -41,7 +41,7 @@ LL | impl<'m, 'l, B, A> FooLifetime<'m, 'l, B, A> {} = help: try `B`, or a name that does not conflict with `FooLifetime`'s generic params error: `FooEnum` has a similarly named generic type parameter `C` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:46:27 + --> tests/ui/mismatching_type_param_order.rs:45:27 | LL | impl FooEnum {} | ^ @@ -49,7 +49,7 @@ LL | impl FooEnum {} = help: try `A`, or a name that does not conflict with `FooEnum`'s generic params error: `FooEnum` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:46:30 + --> tests/ui/mismatching_type_param_order.rs:45:30 | LL | impl FooEnum {} | ^ @@ -57,7 +57,7 @@ LL | impl FooEnum {} = help: try `B`, or a name that does not conflict with `FooEnum`'s generic params error: `FooEnum` has a similarly named generic type parameter `B` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:46:33 + --> tests/ui/mismatching_type_param_order.rs:45:33 | LL | impl FooEnum {} | ^ @@ -65,7 +65,7 @@ LL | impl FooEnum {} = help: try `C`, or a name that does not conflict with `FooEnum`'s generic params error: `FooUnion` has a similarly named generic type parameter `B` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:60:31 + --> tests/ui/mismatching_type_param_order.rs:59:31 | LL | impl FooUnion where A: Copy {} | ^ @@ -73,7 +73,7 @@ LL | impl FooUnion where A: Copy {} = help: try `A`, or a name that does not conflict with `FooUnion`'s generic params error: `FooUnion` has a similarly named generic type parameter `A` in its declaration, but in a different order - --> tests/ui/mismatching_type_param_order.rs:60:34 + --> tests/ui/mismatching_type_param_order.rs:59:34 | LL | impl FooUnion where A: Copy {} | ^ diff --git a/src/tools/clippy/tests/ui/misnamed_getters.fixed b/src/tools/clippy/tests/ui/misnamed_getters.fixed index bc123d1a40ba2..479d5f91492d5 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.fixed +++ b/src/tools/clippy/tests/ui/misnamed_getters.fixed @@ -1,6 +1,5 @@ -#![allow(unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] +#![expect(clippy::struct_field_names)] struct A { a: u8, diff --git a/src/tools/clippy/tests/ui/misnamed_getters.rs b/src/tools/clippy/tests/ui/misnamed_getters.rs index 6590101157c3f..985c273c59ed7 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.rs +++ b/src/tools/clippy/tests/ui/misnamed_getters.rs @@ -1,6 +1,5 @@ -#![allow(unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] +#![expect(clippy::struct_field_names)] struct A { a: u8, diff --git a/src/tools/clippy/tests/ui/misnamed_getters.stderr b/src/tools/clippy/tests/ui/misnamed_getters.stderr index aaf21cecb9255..37f57d7f79e55 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters.stderr +++ b/src/tools/clippy/tests/ui/misnamed_getters.stderr @@ -1,5 +1,5 @@ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:12:5 + --> tests/ui/misnamed_getters.rs:11:5 | LL | / fn a(&self) -> &u8 { LL | | @@ -13,7 +13,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::misnamed_getters)]` error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:17:5 + --> tests/ui/misnamed_getters.rs:16:5 | LL | / fn a_mut(&mut self) -> &mut u8 { LL | | @@ -24,7 +24,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:23:5 + --> tests/ui/misnamed_getters.rs:22:5 | LL | / fn b(self) -> u8 { LL | | @@ -35,7 +35,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:29:5 + --> tests/ui/misnamed_getters.rs:28:5 | LL | / fn b_mut(&mut self) -> &mut u8 { LL | | @@ -46,7 +46,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:35:5 + --> tests/ui/misnamed_getters.rs:34:5 | LL | / fn c(&self) -> &u8 { LL | | @@ -57,7 +57,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:41:5 + --> tests/ui/misnamed_getters.rs:40:5 | LL | / fn c_mut(&mut self) -> &mut u8 { LL | | @@ -68,7 +68,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:54:5 + --> tests/ui/misnamed_getters.rs:53:5 | LL | / unsafe fn a(&self) -> &u8 { LL | | @@ -79,7 +79,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:59:5 + --> tests/ui/misnamed_getters.rs:58:5 | LL | / unsafe fn a_mut(&mut self) -> &mut u8 { LL | | @@ -90,7 +90,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:65:5 + --> tests/ui/misnamed_getters.rs:64:5 | LL | / unsafe fn b(self) -> u8 { LL | | @@ -101,7 +101,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:71:5 + --> tests/ui/misnamed_getters.rs:70:5 | LL | / unsafe fn b_mut(&mut self) -> &mut u8 { LL | | @@ -112,7 +112,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:85:5 + --> tests/ui/misnamed_getters.rs:84:5 | LL | / unsafe fn a_unchecked(&self) -> &u8 { LL | | @@ -123,7 +123,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:90:5 + --> tests/ui/misnamed_getters.rs:89:5 | LL | / unsafe fn a_unchecked_mut(&mut self) -> &mut u8 { LL | | @@ -134,7 +134,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:96:5 + --> tests/ui/misnamed_getters.rs:95:5 | LL | / unsafe fn b_unchecked(self) -> u8 { LL | | @@ -145,7 +145,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:102:5 + --> tests/ui/misnamed_getters.rs:101:5 | LL | / unsafe fn b_unchecked_mut(&mut self) -> &mut u8 { LL | | @@ -156,7 +156,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:136:5 + --> tests/ui/misnamed_getters.rs:135:5 | LL | / fn a(&self) -> &u8 { LL | | @@ -167,7 +167,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:141:5 + --> tests/ui/misnamed_getters.rs:140:5 | LL | / fn a_mut(&mut self) -> &mut u8 { LL | | @@ -178,7 +178,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:147:5 + --> tests/ui/misnamed_getters.rs:146:5 | LL | / fn d(&self) -> &u8 { LL | | @@ -189,7 +189,7 @@ LL | | } | |_____^ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters.rs:152:5 + --> tests/ui/misnamed_getters.rs:151:5 | LL | / fn d_mut(&mut self) -> &mut u8 { LL | | diff --git a/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed b/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed index 7112719a9f284..a923da1962fbe 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed +++ b/src/tools/clippy/tests/ui/misnamed_getters_2021.fixed @@ -1,6 +1,4 @@ //@edition: 2021 -#![allow(unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] // Edition 2021 specific check, where `unsafe` blocks are not required diff --git a/src/tools/clippy/tests/ui/misnamed_getters_2021.rs b/src/tools/clippy/tests/ui/misnamed_getters_2021.rs index 19b5d086041f4..bdb011939da64 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters_2021.rs +++ b/src/tools/clippy/tests/ui/misnamed_getters_2021.rs @@ -1,6 +1,4 @@ //@edition: 2021 -#![allow(unused)] -#![allow(clippy::struct_field_names)] #![warn(clippy::misnamed_getters)] // Edition 2021 specific check, where `unsafe` blocks are not required diff --git a/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr b/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr index 5495e2e3733f0..9771d2d8b119f 100644 --- a/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr +++ b/src/tools/clippy/tests/ui/misnamed_getters_2021.stderr @@ -1,5 +1,5 @@ error: getter function appears to return the wrong field - --> tests/ui/misnamed_getters_2021.rs:15:5 + --> tests/ui/misnamed_getters_2021.rs:13:5 | LL | / unsafe fn a(&self) -> &u8 { LL | | diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed b/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed index 882ff6bf8944a..c5628f2486b0a 100644 --- a/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.1.fixed @@ -1,5 +1,6 @@ +#![warn(clippy::misrefactored_assign_op)] +#![deny(clippy::assign_op_pattern)] #![allow(clippy::eq_op)] -#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] fn main() { let mut a = 5; diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed b/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed index de3a0f1710d24..02c4383f45ab7 100644 --- a/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.2.fixed @@ -1,5 +1,6 @@ +#![warn(clippy::misrefactored_assign_op)] +#![deny(clippy::assign_op_pattern)] #![allow(clippy::eq_op)] -#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] fn main() { let mut a = 5; diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.rs b/src/tools/clippy/tests/ui/misrefactored_assign_op.rs index 62d83d1619c1e..dcb284574ce43 100644 --- a/src/tools/clippy/tests/ui/misrefactored_assign_op.rs +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.rs @@ -1,5 +1,6 @@ +#![warn(clippy::misrefactored_assign_op)] +#![deny(clippy::assign_op_pattern)] #![allow(clippy::eq_op)] -#![warn(clippy::misrefactored_assign_op, clippy::assign_op_pattern)] fn main() { let mut a = 5; diff --git a/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr b/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr index 63f3a3e28f12c..50547a91dedc1 100644 --- a/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr +++ b/src/tools/clippy/tests/ui/misrefactored_assign_op.stderr @@ -1,5 +1,5 @@ error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:6:5 + --> tests/ui/misrefactored_assign_op.rs:7:5 | LL | a += a + 1; | ^^^^^^^^^^ @@ -18,7 +18,7 @@ LL + a = a + a + 1; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:9:5 + --> tests/ui/misrefactored_assign_op.rs:10:5 | LL | a += 1 + a; | ^^^^^^^^^^ @@ -35,7 +35,7 @@ LL + a = a + 1 + a; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:12:5 + --> tests/ui/misrefactored_assign_op.rs:13:5 | LL | a -= a - 1; | ^^^^^^^^^^ @@ -52,7 +52,7 @@ LL + a = a - (a - 1); | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:15:5 + --> tests/ui/misrefactored_assign_op.rs:16:5 | LL | a *= a * 99; | ^^^^^^^^^^^ @@ -69,7 +69,7 @@ LL + a = a * a * 99; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:18:5 + --> tests/ui/misrefactored_assign_op.rs:19:5 | LL | a *= 42 * a; | ^^^^^^^^^^^ @@ -86,7 +86,7 @@ LL + a = a * 42 * a; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:21:5 + --> tests/ui/misrefactored_assign_op.rs:22:5 | LL | a /= a / 2; | ^^^^^^^^^^ @@ -103,7 +103,7 @@ LL + a = a / (a / 2); | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:24:5 + --> tests/ui/misrefactored_assign_op.rs:25:5 | LL | a %= a % 5; | ^^^^^^^^^^ @@ -120,7 +120,7 @@ LL + a = a % (a % 5); | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:27:5 + --> tests/ui/misrefactored_assign_op.rs:28:5 | LL | a &= a & 1; | ^^^^^^^^^^ @@ -137,7 +137,7 @@ LL + a = a & a & 1; | error: variable appears on both sides of an assignment operation - --> tests/ui/misrefactored_assign_op.rs:30:5 + --> tests/ui/misrefactored_assign_op.rs:31:5 | LL | a *= a * a; | ^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs index eb98969efa47f..b8cdb71bd30a9 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.rs @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::missing_asserts_for_indexing)] fn sum(v: &[u8]) -> u8 { diff --git a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr index 2929646494a41..fe929aa36dbad 100644 --- a/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr +++ b/src/tools/clippy/tests/ui/missing_asserts_for_indexing_unfixable.stderr @@ -1,5 +1,5 @@ error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:5:5 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:4:5 | LL | v[0] + v[1] + v[2] + v[3] + v[4] | ^^^^ ^^^^ ^^^^ ^^^^ ^^^^ @@ -10,7 +10,7 @@ LL | v[0] + v[1] + v[2] + v[3] + v[4] = help: to override `-D warnings` add `#[allow(clippy::missing_asserts_for_indexing)]` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:10:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:9:13 | LL | let _ = v[0]; | ^^^^ @@ -21,7 +21,7 @@ LL | let _ = v[1..4]; = help: consider asserting the length before indexing: `assert!(v.len() > 3);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:17:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:16:13 | LL | let a = v[0]; | ^^^^ @@ -34,7 +34,7 @@ LL | let c = v[2]; = help: consider asserting the length before indexing: `assert!(v.len() > 2);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:26:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:25:13 | LL | let _ = v1[0] + v1[12]; | ^^^^^ ^^^^^^ @@ -42,7 +42,7 @@ LL | let _ = v1[0] + v1[12]; = help: consider asserting the length before indexing: `assert!(v1.len() > 12);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:28:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:27:13 | LL | let _ = v2[5] + v2[15]; | ^^^^^ ^^^^^^ @@ -50,7 +50,7 @@ LL | let _ = v2[5] + v2[15]; = help: consider asserting the length before indexing: `assert!(v2.len() > 15);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:35:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:34:13 | LL | let _ = v2[5] + v2[15]; | ^^^^^ ^^^^^^ @@ -58,7 +58,7 @@ LL | let _ = v2[5] + v2[15]; = help: consider asserting the length before indexing: `assert!(v2.len() > 15);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:45:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:44:13 | LL | let _ = f.v[0] + f.v[1]; | ^^^^^^ ^^^^^^ @@ -66,7 +66,7 @@ LL | let _ = f.v[0] + f.v[1]; = help: consider asserting the length before indexing: `assert!(f.v.len() > 1);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:59:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:58:13 | LL | let _ = x[0] + x[1]; | ^^^^ ^^^^ @@ -74,7 +74,7 @@ LL | let _ = x[0] + x[1]; = help: consider asserting the length before indexing: `assert!(x.len() > 1);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:77:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:76:13 | LL | let _ = v1[1] + v1[2]; | ^^^^^ ^^^^^ @@ -82,7 +82,7 @@ LL | let _ = v1[1] + v1[2]; = help: consider asserting the length before indexing: `assert!(v1.len() > 2);` error: indexing into a slice multiple times without an `assert` - --> tests/ui/missing_asserts_for_indexing_unfixable.rs:85:13 + --> tests/ui/missing_asserts_for_indexing_unfixable.rs:84:13 | LL | let _ = v1[0] + v1[1] + v1[2]; | ^^^^^ ^^^^^ ^^^^^ diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed index 46a7f79ac16ce..bc2d5b9d1c7b4 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed @@ -1,5 +1,5 @@ #![warn(clippy::missing_const_for_fn)] -#![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)] +#![expect(clippy::let_and_return)] #![feature(const_trait_impl)] use std::mem::transmute; diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs index 78e1939a85973..26baebfc535e2 100644 --- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs +++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs @@ -1,5 +1,5 @@ #![warn(clippy::missing_const_for_fn)] -#![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)] +#![expect(clippy::let_and_return)] #![feature(const_trait_impl)] use std::mem::transmute; diff --git a/src/tools/clippy/tests/ui/missing_fields_in_debug.rs b/src/tools/clippy/tests/ui/missing_fields_in_debug.rs index b206f4d8f8ce3..86c97ce32b687 100644 --- a/src/tools/clippy/tests/ui/missing_fields_in_debug.rs +++ b/src/tools/clippy/tests/ui/missing_fields_in_debug.rs @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::missing_fields_in_debug)] use std::fmt; diff --git a/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr b/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr index 75b551e1f5c7e..4ea837e47836d 100644 --- a/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr +++ b/src/tools/clippy/tests/ui/missing_fields_in_debug.stderr @@ -1,5 +1,5 @@ error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:14:1 + --> tests/ui/missing_fields_in_debug.rs:13:1 | LL | / impl fmt::Debug for NamedStruct1Ignored { ... | @@ -7,7 +7,7 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:11:5 + --> tests/ui/missing_fields_in_debug.rs:10:5 | LL | hidden: u32, | ^^^^^^^^^^^ @@ -17,7 +17,7 @@ LL | hidden: u32, = help: to override `-D warnings` add `#[allow(clippy::missing_fields_in_debug)]` error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:34:1 + --> tests/ui/missing_fields_in_debug.rs:33:1 | LL | / impl fmt::Debug for NamedStructMultipleIgnored { ... | @@ -25,17 +25,17 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:28:5 + --> tests/ui/missing_fields_in_debug.rs:27:5 | LL | hidden: u32, | ^^^^^^^^^^^ note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:29:5 + --> tests/ui/missing_fields_in_debug.rs:28:5 | LL | hidden2: String, | ^^^^^^^^^^^^^^^ note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:31:5 + --> tests/ui/missing_fields_in_debug.rs:30:5 | LL | hidden4: ((((u8), u16), u32), u64), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -43,7 +43,7 @@ LL | hidden4: ((((u8), u16), u32), u64), = help: consider calling `.finish_non_exhaustive()` if you intend to ignore fields error: manual `Debug` impl does not include all fields - --> tests/ui/missing_fields_in_debug.rs:97:1 + --> tests/ui/missing_fields_in_debug.rs:96:1 | LL | / impl fmt::Debug for MultiExprDebugImpl { LL | | @@ -54,7 +54,7 @@ LL | | } | |_^ | note: this field is unused - --> tests/ui/missing_fields_in_debug.rs:93:5 + --> tests/ui/missing_fields_in_debug.rs:92:5 | LL | b: String, | ^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/missing_inline.rs b/src/tools/clippy/tests/ui/missing_inline.rs index 8e937d609512a..0adaeaa9cab8a 100644 --- a/src/tools/clippy/tests/ui/missing_inline.rs +++ b/src/tools/clippy/tests/ui/missing_inline.rs @@ -2,7 +2,7 @@ #![crate_type = "dylib"] // When denying at the crate level, be sure to not get random warnings from the // injected intrinsics by the compiler. -#![allow(dead_code, non_snake_case)] +#![expect(non_snake_case)] type Typedef = String; pub type PubTypedef = String; diff --git a/src/tools/clippy/tests/ui/missing_inline.stderr b/src/tools/clippy/tests/ui/missing_inline.stderr index 99ee828e805ca..9f5987fb8e79b 100644 --- a/src/tools/clippy/tests/ui/missing_inline.stderr +++ b/src/tools/clippy/tests/ui/missing_inline.stderr @@ -1,4 +1,4 @@ -error: missing `#[inline]` for a function +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:20:1 | LL | pub fn pub_foo() {} @@ -7,31 +7,31 @@ LL | pub fn pub_foo() {} = note: `-D clippy::missing-inline-in-public-items` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::missing_inline_in_public_items)]` -error: missing `#[inline]` for a default trait method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:39:5 | LL | fn PubBar_b() {} | ^^^^^^^^^^^^^^^^ -error: missing `#[inline]` for a method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:56:5 | LL | fn PubBar_a() {} | ^^^^^^^^^^^^^^^^ -error: missing `#[inline]` for a method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:60:5 | LL | fn PubBar_b() {} | ^^^^^^^^^^^^^^^^ -error: missing `#[inline]` for a method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:64:5 | LL | fn PubBar_c() {} | ^^^^^^^^^^^^^^^^ -error: missing `#[inline]` for a method +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline.rs:76:5 | LL | pub fn PubFooImpl() {} diff --git a/src/tools/clippy/tests/ui/missing_inline_executable.stderr b/src/tools/clippy/tests/ui/missing_inline_executable.stderr index 3108e4e490659..d083cb2b54915 100644 --- a/src/tools/clippy/tests/ui/missing_inline_executable.stderr +++ b/src/tools/clippy/tests/ui/missing_inline_executable.stderr @@ -1,4 +1,4 @@ -error: missing `#[inline]` for a function +error: missing `#[inline]` on a publicly callable function --> tests/ui/missing_inline_executable.rs:3:1 | LL | pub fn foo() {} diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.rs b/src/tools/clippy/tests/ui/missing_panics_doc.rs index d016e099e303b..63e84ffaab634 100644 --- a/src/tools/clippy/tests/ui/missing_panics_doc.rs +++ b/src/tools/clippy/tests/ui/missing_panics_doc.rs @@ -1,6 +1,6 @@ //@aux-build:macro_rules.rs #![warn(clippy::missing_panics_doc)] -#![allow(clippy::option_map_unit_fn, clippy::unnecessary_literal_unwrap)] +#![expect(clippy::option_map_unit_fn, clippy::unnecessary_literal_unwrap)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/missing_spin_loop.fixed b/src/tools/clippy/tests/ui/missing_spin_loop.fixed index 03fbf8ccc8a37..7876a0c47d5e7 100644 --- a/src/tools/clippy/tests/ui/missing_spin_loop.fixed +++ b/src/tools/clippy/tests/ui/missing_spin_loop.fixed @@ -1,6 +1,5 @@ #![warn(clippy::missing_spin_loop)] -#![allow(clippy::bool_comparison)] -#![allow(unused_braces)] +#![expect(clippy::bool_comparison)] use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/src/tools/clippy/tests/ui/missing_spin_loop.rs b/src/tools/clippy/tests/ui/missing_spin_loop.rs index bf18590b9408e..06bfe8eeaab80 100644 --- a/src/tools/clippy/tests/ui/missing_spin_loop.rs +++ b/src/tools/clippy/tests/ui/missing_spin_loop.rs @@ -1,6 +1,5 @@ #![warn(clippy::missing_spin_loop)] -#![allow(clippy::bool_comparison)] -#![allow(unused_braces)] +#![expect(clippy::bool_comparison)] use core::sync::atomic::{AtomicBool, Ordering}; diff --git a/src/tools/clippy/tests/ui/missing_spin_loop.stderr b/src/tools/clippy/tests/ui/missing_spin_loop.stderr index 98c32d0423469..d4a93bb2d75b7 100644 --- a/src/tools/clippy/tests/ui/missing_spin_loop.stderr +++ b/src/tools/clippy/tests/ui/missing_spin_loop.stderr @@ -1,5 +1,5 @@ error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:10:37 + --> tests/ui/missing_spin_loop.rs:9:37 | LL | while b.load(Ordering::Acquire) {} | ^^ help: try: `{ std::hint::spin_loop() }` @@ -8,31 +8,31 @@ LL | while b.load(Ordering::Acquire) {} = help: to override `-D warnings` add `#[allow(clippy::missing_spin_loop)]` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:13:37 + --> tests/ui/missing_spin_loop.rs:12:37 | LL | while !b.load(Ordering::SeqCst) {} | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:16:46 + --> tests/ui/missing_spin_loop.rs:15:46 | LL | while b.load(Ordering::Acquire) == false {} | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:19:49 + --> tests/ui/missing_spin_loop.rs:18:49 | LL | while { true == b.load(Ordering::Acquire) } {} | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:22:93 + --> tests/ui/missing_spin_loop.rs:21:93 | LL | while b.compare_exchange(true, false, Ordering::Acquire, Ordering::Relaxed) != Ok(true) {} | ^^ help: try: `{ std::hint::spin_loop() }` error: busy-waiting loop should at least have a spin loop hint - --> tests/ui/missing_spin_loop.rs:25:94 + --> tests/ui/missing_spin_loop.rs:24:94 | LL | while Ok(false) != b.compare_exchange(false, true, Ordering::Acquire, Ordering::Relaxed) {} | ^^ help: try: `{ std::hint::spin_loop() }` diff --git a/src/tools/clippy/tests/ui/missing_trait_methods.rs b/src/tools/clippy/tests/ui/missing_trait_methods.rs index 67070a2999510..90349a0de101f 100644 --- a/src/tools/clippy/tests/ui/missing_trait_methods.rs +++ b/src/tools/clippy/tests/ui/missing_trait_methods.rs @@ -1,5 +1,5 @@ -#![allow(unused, clippy::needless_lifetimes)] #![warn(clippy::missing_trait_methods)] +#![expect(clippy::needless_lifetimes)] trait A { fn provided() {} diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed b/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed index 58faeaee09d46..2602a2fda5135 100644 --- a/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations.fixed @@ -1,7 +1,7 @@ //@aux-build:macro_rules.rs #![warn(clippy::missing_transmute_annotations)] -#![allow(clippy::let_with_type_underscore)] +#![expect(clippy::let_with_type_underscore)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations.rs b/src/tools/clippy/tests/ui/missing_transmute_annotations.rs index c9a4c5fa83b2b..fe7b7238d33fe 100644 --- a/src/tools/clippy/tests/ui/missing_transmute_annotations.rs +++ b/src/tools/clippy/tests/ui/missing_transmute_annotations.rs @@ -1,7 +1,7 @@ //@aux-build:macro_rules.rs #![warn(clippy::missing_transmute_annotations)] -#![allow(clippy::let_with_type_underscore)] +#![expect(clippy::let_with_type_underscore)] #[macro_use] extern crate macro_rules; diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed index a0190acc5d4b3..3a7d714375a77 100644 --- a/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.fixed @@ -1,10 +1,8 @@ //@aux-build: proc_macros.rs -#![allow( - dead_code, - unused_variables, +#![warn(clippy::mistyped_literal_suffixes)] +#![expect( overflowing_literals, - clippy::excessive_precision, clippy::inconsistent_digit_grouping, clippy::unusual_byte_groupings )] diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs index 12a26204e7557..02e76a468d210 100644 --- a/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.rs @@ -1,10 +1,8 @@ //@aux-build: proc_macros.rs -#![allow( - dead_code, - unused_variables, +#![warn(clippy::mistyped_literal_suffixes)] +#![expect( overflowing_literals, - clippy::excessive_precision, clippy::inconsistent_digit_grouping, clippy::unusual_byte_groupings )] diff --git a/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr b/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr index 9c6b2fab21c31..a986bc0e69ec0 100644 --- a/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr +++ b/src/tools/clippy/tests/ui/mistyped_literal_suffix.stderr @@ -1,97 +1,98 @@ error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:16:18 + --> tests/ui/mistyped_literal_suffix.rs:14:18 | LL | let fail14 = 2_32; | ^^^^ help: did you mean to write: `2_i32` | - = note: `#[deny(clippy::mistyped_literal_suffixes)]` on by default + = note: `-D clippy::mistyped-literal-suffixes` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::mistyped_literal_suffixes)]` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:18:18 + --> tests/ui/mistyped_literal_suffix.rs:16:18 | LL | let fail15 = 4_64; | ^^^^ help: did you mean to write: `4_i64` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:20:18 + --> tests/ui/mistyped_literal_suffix.rs:18:18 | LL | let fail16 = 7_8; // | ^^^ help: did you mean to write: `7_i8` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:23:18 + --> tests/ui/mistyped_literal_suffix.rs:21:18 | LL | let fail17 = 23_16; // | ^^^^^ help: did you mean to write: `23_i16` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:28:18 + --> tests/ui/mistyped_literal_suffix.rs:26:18 | LL | let fail20 = 2__8; // | ^^^^ help: did you mean to write: `2_i8` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:31:18 + --> tests/ui/mistyped_literal_suffix.rs:29:18 | LL | let fail21 = 4___16; // | ^^^^^^ help: did you mean to write: `4_i16` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:36:18 + --> tests/ui/mistyped_literal_suffix.rs:34:18 | LL | let fail25 = 1E2_32; | ^^^^^^ help: did you mean to write: `1E2_f32` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:38:18 + --> tests/ui/mistyped_literal_suffix.rs:36:18 | LL | let fail26 = 43E7_64; | ^^^^^^^ help: did you mean to write: `43E7_f64` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:40:18 + --> tests/ui/mistyped_literal_suffix.rs:38:18 | LL | let fail27 = 243E17_32; | ^^^^^^^^^ help: did you mean to write: `243E17_f32` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:42:18 + --> tests/ui/mistyped_literal_suffix.rs:40:18 | LL | let fail28 = 241251235E723_64; | ^^^^^^^^^^^^^^^^ help: did you mean to write: `241_251_235E723_f64` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:47:18 + --> tests/ui/mistyped_literal_suffix.rs:45:18 | LL | let fail30 = 127_8; // should be i8 | ^^^^^ help: did you mean to write: `127_i8` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:50:18 + --> tests/ui/mistyped_literal_suffix.rs:48:18 | LL | let fail31 = 240_8; // should be u8 | ^^^^^ help: did you mean to write: `240_u8` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:54:18 + --> tests/ui/mistyped_literal_suffix.rs:52:18 | LL | let fail33 = 0x1234_16; | ^^^^^^^^^ help: did you mean to write: `0x1234_i16` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:56:18 + --> tests/ui/mistyped_literal_suffix.rs:54:18 | LL | let fail34 = 0xABCD_16; | ^^^^^^^^^ help: did you mean to write: `0xABCD_u16` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:59:18 + --> tests/ui/mistyped_literal_suffix.rs:57:18 | LL | let fail36 = 0xFFFF_FFFF_FFFF_FFFF_64; // u64 | ^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to write: `0xFFFF_FFFF_FFFF_FFFF_u64` error: mistyped literal suffix - --> tests/ui/mistyped_literal_suffix.rs:67:13 + --> tests/ui/mistyped_literal_suffix.rs:65:13 | LL | let _ = 1.12345E1_32; | ^^^^^^^^^^^^ help: did you mean to write: `1.123_45E1_f32` diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.rs b/src/tools/clippy/tests/ui/module_name_repetitions.rs index 5d16858bf85ec..8fdce71d05528 100644 --- a/src/tools/clippy/tests/ui/module_name_repetitions.rs +++ b/src/tools/clippy/tests/ui/module_name_repetitions.rs @@ -1,7 +1,6 @@ //@compile-flags: --test #![warn(clippy::module_name_repetitions)] -#![allow(dead_code)] pub mod foo { pub fn foo() {} diff --git a/src/tools/clippy/tests/ui/module_name_repetitions.stderr b/src/tools/clippy/tests/ui/module_name_repetitions.stderr index 9000c44a3902e..e4c3daacdbead 100644 --- a/src/tools/clippy/tests/ui/module_name_repetitions.stderr +++ b/src/tools/clippy/tests/ui/module_name_repetitions.stderr @@ -1,5 +1,5 @@ error: item name starts with its containing module's name - --> tests/ui/module_name_repetitions.rs:8:12 + --> tests/ui/module_name_repetitions.rs:7:12 | LL | pub fn foo_bar() {} | ^^^^^^^ @@ -8,31 +8,31 @@ LL | pub fn foo_bar() {} = help: to override `-D warnings` add `#[allow(clippy::module_name_repetitions)]` error: item name ends with its containing module's name - --> tests/ui/module_name_repetitions.rs:11:12 + --> tests/ui/module_name_repetitions.rs:10:12 | LL | pub fn bar_foo() {} | ^^^^^^^ error: item name starts with its containing module's name - --> tests/ui/module_name_repetitions.rs:14:16 + --> tests/ui/module_name_repetitions.rs:13:16 | LL | pub struct FooCake; | ^^^^^^^ error: item name ends with its containing module's name - --> tests/ui/module_name_repetitions.rs:17:14 + --> tests/ui/module_name_repetitions.rs:16:14 | LL | pub enum CakeFoo {} | ^^^^^^^ error: item name starts with its containing module's name - --> tests/ui/module_name_repetitions.rs:20:16 + --> tests/ui/module_name_repetitions.rs:19:16 | LL | pub struct Foo7Bar; | ^^^^^^^ error: item name starts with its containing module's name - --> tests/ui/module_name_repetitions.rs:33:20 + --> tests/ui/module_name_repetitions.rs:32:20 | LL | pub use error::FooError; | ^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs index 0e08174f686eb..46d554616f8df 100644 --- a/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_float.rs @@ -1,7 +1,7 @@ #![feature(f128)] #![feature(f16)] #![warn(clippy::modulo_arithmetic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)] +#![expect(clippy::no_effect)] fn main() { // Lint when both sides are const and of the opposite sign diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs index d7cd910e43cb7..583637dbc2855 100644 --- a/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs @@ -1,5 +1,5 @@ #![warn(clippy::modulo_arithmetic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)] +#![expect(clippy::no_effect)] fn main() { // Lint on signed integral numbers diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs index 9f5fb697df0ea..160bc86629247 100644 --- a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.rs @@ -1,10 +1,5 @@ #![warn(clippy::modulo_arithmetic)] -#![allow( - clippy::no_effect, - clippy::unnecessary_operation, - clippy::modulo_one, - clippy::identity_op -)] +#![expect(clippy::identity_op, clippy::modulo_one, clippy::no_effect)] fn main() { // Lint when both sides are const and of the opposite sign diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr index 12edee8cf4255..e09673213f79a 100644 --- a/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr +++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral_const.stderr @@ -1,5 +1,5 @@ error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:11:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:6:5 | LL | -1 % 2; | ^^^^^^ @@ -10,7 +10,7 @@ LL | -1 % 2; = help: to override `-D warnings` add `#[allow(clippy::modulo_arithmetic)]` error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:14:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:9:5 | LL | 1 % -2; | ^^^^^^ @@ -19,7 +19,7 @@ LL | 1 % -2; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 3` - --> tests/ui/modulo_arithmetic_integral_const.rs:17:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:12:5 | LL | (1 - 2) % (1 + 2); | ^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | (1 - 2) % (1 + 2); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `3 % -1` - --> tests/ui/modulo_arithmetic_integral_const.rs:20:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:15:5 | LL | (1 + 2) % (1 - 2); | ^^^^^^^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | (1 + 2) % (1 - 2); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-35 % 300000` - --> tests/ui/modulo_arithmetic_integral_const.rs:23:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:18:5 | LL | 35 * (7 - 4 * 2) % (-500 * -600); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -46,7 +46,7 @@ LL | 35 * (7 - 4 * 2) % (-500 * -600); = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:26:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:21:5 | LL | -1i8 % 2i8; | ^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | -1i8 % 2i8; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:29:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:24:5 | LL | 1i8 % -2i8; | ^^^^^^^^^^ @@ -64,7 +64,7 @@ LL | 1i8 % -2i8; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:32:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:27:5 | LL | -1i16 % 2i16; | ^^^^^^^^^^^^ @@ -73,7 +73,7 @@ LL | -1i16 % 2i16; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:35:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:30:5 | LL | 1i16 % -2i16; | ^^^^^^^^^^^^ @@ -82,7 +82,7 @@ LL | 1i16 % -2i16; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:38:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:33:5 | LL | -1i32 % 2i32; | ^^^^^^^^^^^^ @@ -91,7 +91,7 @@ LL | -1i32 % 2i32; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:41:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:36:5 | LL | 1i32 % -2i32; | ^^^^^^^^^^^^ @@ -100,7 +100,7 @@ LL | 1i32 % -2i32; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:44:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:39:5 | LL | -1i64 % 2i64; | ^^^^^^^^^^^^ @@ -109,7 +109,7 @@ LL | -1i64 % 2i64; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:47:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:42:5 | LL | 1i64 % -2i64; | ^^^^^^^^^^^^ @@ -118,7 +118,7 @@ LL | 1i64 % -2i64; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:50:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:45:5 | LL | -1i128 % 2i128; | ^^^^^^^^^^^^^^ @@ -127,7 +127,7 @@ LL | -1i128 % 2i128; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:53:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:48:5 | LL | 1i128 % -2i128; | ^^^^^^^^^^^^^^ @@ -136,7 +136,7 @@ LL | 1i128 % -2i128; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `-1 % 2` - --> tests/ui/modulo_arithmetic_integral_const.rs:56:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:51:5 | LL | -1isize % 2isize; | ^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | -1isize % 2isize; = note: or consider using `rem_euclid` or similar function error: you are using modulo operator on constants with different signs: `1 % -2` - --> tests/ui/modulo_arithmetic_integral_const.rs:59:5 + --> tests/ui/modulo_arithmetic_integral_const.rs:54:5 | LL | 1isize % -2isize; | ^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/modulo_one.rs b/src/tools/clippy/tests/ui/modulo_one.rs index 63ae61059cb86..730c37d0e368e 100644 --- a/src/tools/clippy/tests/ui/modulo_one.rs +++ b/src/tools/clippy/tests/ui/modulo_one.rs @@ -1,6 +1,5 @@ #![warn(clippy::modulo_one)] -#![allow(unconditional_panic)] -#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::identity_op)] +#![expect(clippy::identity_op, clippy::no_effect)] static STATIC_ONE: usize = 2 - 1; static STATIC_NEG_ONE: i64 = 1 - 2; diff --git a/src/tools/clippy/tests/ui/modulo_one.stderr b/src/tools/clippy/tests/ui/modulo_one.stderr index 1c9c79d1c6214..f841ac18bbc3d 100644 --- a/src/tools/clippy/tests/ui/modulo_one.stderr +++ b/src/tools/clippy/tests/ui/modulo_one.stderr @@ -1,5 +1,5 @@ error: any number modulo 1 will be 0 - --> tests/ui/modulo_one.rs:9:5 + --> tests/ui/modulo_one.rs:8:5 | LL | 10 % 1; | ^^^^^^ @@ -8,31 +8,31 @@ LL | 10 % 1; = help: to override `-D warnings` add `#[allow(clippy::modulo_one)]` error: any number modulo -1 will panic/overflow or result in 0 - --> tests/ui/modulo_one.rs:12:5 + --> tests/ui/modulo_one.rs:11:5 | LL | 10 % -1; | ^^^^^^^ error: any number modulo -1 will panic/overflow or result in 0 - --> tests/ui/modulo_one.rs:17:5 + --> tests/ui/modulo_one.rs:16:5 | LL | i32::MIN % (-1); | ^^^^^^^^^^^^^^^ error: any number modulo 1 will be 0 - --> tests/ui/modulo_one.rs:24:5 + --> tests/ui/modulo_one.rs:23:5 | LL | 2 % ONE; | ^^^^^^^ error: any number modulo -1 will panic/overflow or result in 0 - --> tests/ui/modulo_one.rs:29:5 + --> tests/ui/modulo_one.rs:28:5 | LL | 2 % NEG_ONE; | ^^^^^^^^^^^ error: any number modulo -1 will panic/overflow or result in 0 - --> tests/ui/modulo_one.rs:35:5 + --> tests/ui/modulo_one.rs:34:5 | LL | INT_MIN % NEG_ONE; | ^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/msrv_attributes_without_early_lints.rs b/src/tools/clippy/tests/ui/msrv_attributes_without_early_lints.rs index dcef1a485fceb..fe83023aaeb79 100644 --- a/src/tools/clippy/tests/ui/msrv_attributes_without_early_lints.rs +++ b/src/tools/clippy/tests/ui/msrv_attributes_without_early_lints.rs @@ -1,6 +1,5 @@ //@check-pass -#![allow(clippy::all, clippy::pedantic, clippy::restriction, clippy::nursery)] #![forbid(clippy::ptr_as_ptr)] /// MSRV checking in late passes skips checking the parent nodes if no early pass sees a diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs index 0ff881472cbb9..5d51c47eeed91 100644 --- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs +++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs @@ -1,11 +1,11 @@ //@needs-asm-support //@aux-build:proc_macros.rs +#![warn(clippy::multiple_unsafe_ops_per_block)] #![expect( dropping_copy_types, - clippy::unnecessary_operation, - clippy::unnecessary_literal_unwrap + clippy::unnecessary_literal_unwrap, + clippy::unnecessary_operation )] -#![warn(clippy::multiple_unsafe_ops_per_block)] extern crate proc_macros; use proc_macros::external; diff --git a/src/tools/clippy/tests/ui/must_use_candidates.fixed b/src/tools/clippy/tests/ui/must_use_candidates.fixed index 17569884658b1..226fafccddced 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.fixed +++ b/src/tools/clippy/tests/ui/must_use_candidates.fixed @@ -1,10 +1,4 @@ #![feature(never_type)] -#![allow( - unused_mut, - clippy::redundant_allocation, - clippy::needless_pass_by_ref_mut, - static_mut_refs -)] #![warn(clippy::must_use_candidate)] use std::rc::Rc; use std::sync::Arc; diff --git a/src/tools/clippy/tests/ui/must_use_candidates.rs b/src/tools/clippy/tests/ui/must_use_candidates.rs index 8b898533d01b7..f9b6c2986504c 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.rs +++ b/src/tools/clippy/tests/ui/must_use_candidates.rs @@ -1,10 +1,4 @@ #![feature(never_type)] -#![allow( - unused_mut, - clippy::redundant_allocation, - clippy::needless_pass_by_ref_mut, - static_mut_refs -)] #![warn(clippy::must_use_candidate)] use std::rc::Rc; use std::sync::Arc; diff --git a/src/tools/clippy/tests/ui/must_use_candidates.stderr b/src/tools/clippy/tests/ui/must_use_candidates.stderr index 38c7334322503..24fda87cd16aa 100644 --- a/src/tools/clippy/tests/ui/must_use_candidates.stderr +++ b/src/tools/clippy/tests/ui/must_use_candidates.stderr @@ -1,5 +1,5 @@ error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:16:8 + --> tests/ui/must_use_candidates.rs:10:8 | LL | pub fn pure(i: u8) -> u8 { | ^^^^ @@ -13,7 +13,7 @@ LL | pub fn pure(i: u8) -> u8 { | error: this method could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:22:12 + --> tests/ui/must_use_candidates.rs:16:12 | LL | pub fn inherent_pure(&self) -> u8 { | ^^^^^^^^^^^^^ @@ -25,7 +25,7 @@ LL ~ pub fn inherent_pure(&self) -> u8 { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:54:8 + --> tests/ui/must_use_candidates.rs:48:8 | LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { | ^^^^^^^^^^^ @@ -37,7 +37,7 @@ LL | pub fn with_marker(_d: std::marker::PhantomData<&mut u32>) -> bool { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:67:8 + --> tests/ui/must_use_candidates.rs:61:8 | LL | pub fn rcd(_x: Rc) -> bool { | ^^^ @@ -49,7 +49,7 @@ LL | pub fn rcd(_x: Rc) -> bool { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:76:8 + --> tests/ui/must_use_candidates.rs:70:8 | LL | pub fn arcd(_x: Arc) -> bool { | ^^^^ @@ -61,7 +61,7 @@ LL | pub fn arcd(_x: Arc) -> bool { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:108:8 + --> tests/ui/must_use_candidates.rs:102:8 | LL | pub fn result_uninhabited() -> Result { | ^^^^^^^^^^^^^^^^^^ @@ -74,7 +74,7 @@ LL | pub fn result_uninhabited() -> Result { | error: this function could have a `#[must_use]` attribute - --> tests/ui/must_use_candidates.rs:113:8 + --> tests/ui/must_use_candidates.rs:107:8 | LL | pub fn controlflow_uninhabited() -> std::ops::ControlFlow { | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/must_use_unit.fixed b/src/tools/clippy/tests/ui/must_use_unit.fixed index b9871991b16f0..5c1993f6575a2 100644 --- a/src/tools/clippy/tests/ui/must_use_unit.fixed +++ b/src/tools/clippy/tests/ui/must_use_unit.fixed @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![warn(clippy::must_use_unit)] -#![allow(clippy::unused_unit)] +#![expect(clippy::unused_unit)] extern crate proc_macros; use proc_macros::external; diff --git a/src/tools/clippy/tests/ui/must_use_unit.rs b/src/tools/clippy/tests/ui/must_use_unit.rs index a5865681c8a8e..86b4380a477aa 100644 --- a/src/tools/clippy/tests/ui/must_use_unit.rs +++ b/src/tools/clippy/tests/ui/must_use_unit.rs @@ -1,7 +1,7 @@ //@aux-build:proc_macros.rs #![warn(clippy::must_use_unit)] -#![allow(clippy::unused_unit)] +#![expect(clippy::unused_unit)] extern crate proc_macros; use proc_macros::external; diff --git a/src/tools/clippy/tests/ui/mut_from_ref.rs b/src/tools/clippy/tests/ui/mut_from_ref.rs index 1b0b351518cbb..f91e2992406b7 100644 --- a/src/tools/clippy/tests/ui/mut_from_ref.rs +++ b/src/tools/clippy/tests/ui/mut_from_ref.rs @@ -1,11 +1,10 @@ -#![allow( - unused, +#![warn(clippy::mut_from_ref)] +#![expect( + clippy::boxed_local, clippy::needless_lifetimes, clippy::needless_pass_by_ref_mut, - clippy::redundant_allocation, - clippy::boxed_local + clippy::redundant_allocation )] -#![warn(clippy::mut_from_ref)] struct Foo; diff --git a/src/tools/clippy/tests/ui/mut_from_ref.stderr b/src/tools/clippy/tests/ui/mut_from_ref.stderr index 0974268734653..92165eb9ae735 100644 --- a/src/tools/clippy/tests/ui/mut_from_ref.stderr +++ b/src/tools/clippy/tests/ui/mut_from_ref.stderr @@ -1,11 +1,11 @@ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:13:39 + --> tests/ui/mut_from_ref.rs:12:39 | LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:13:29 + --> tests/ui/mut_from_ref.rs:12:29 | LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { | ^^^^^ @@ -13,85 +13,85 @@ LL | fn this_wont_hurt_a_bit(&self) -> &mut Foo { = help: to override `-D warnings` add `#[allow(clippy::mut_from_ref)]` error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:21:25 + --> tests/ui/mut_from_ref.rs:20:25 | LL | fn ouch(x: &Foo) -> &mut Foo; | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:21:16 + --> tests/ui/mut_from_ref.rs:20:16 | LL | fn ouch(x: &Foo) -> &mut Foo; | ^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:31:21 + --> tests/ui/mut_from_ref.rs:30:21 | LL | fn fail(x: &u32) -> &mut u16 { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:31:12 + --> tests/ui/mut_from_ref.rs:30:12 | LL | fn fail(x: &u32) -> &mut u16 { | ^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:37:50 + --> tests/ui/mut_from_ref.rs:36:50 | LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:37:25 + --> tests/ui/mut_from_ref.rs:36:25 | LL | fn fail_lifetime<'a>(x: &'a u32, y: &mut u32) -> &'a mut u32 { | ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:43:67 + --> tests/ui/mut_from_ref.rs:42:67 | LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:43:27 + --> tests/ui/mut_from_ref.rs:42:27 | LL | fn fail_double<'a, 'b>(x: &'a u32, y: &'a u32, z: &'b mut u32) -> &'a mut u32 { | ^^^^^^^ ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:49:46 + --> tests/ui/mut_from_ref.rs:48:46 | LL | fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:49:24 + --> tests/ui/mut_from_ref.rs:48:24 | LL | fn fail_tuples<'a>(x: (&'a u32, &'a u32)) -> &'a mut u32 { | ^^^^^^^ ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:55:37 + --> tests/ui/mut_from_ref.rs:54:37 | LL | fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { | ^^^^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:55:24 + --> tests/ui/mut_from_ref.rs:54:24 | LL | fn fail_box<'a>(x: Box<&'a u32>) -> &'a mut u32 { | ^^^^^^^ error: mutable borrow from immutable input(s) - --> tests/ui/mut_from_ref.rs:85:35 + --> tests/ui/mut_from_ref.rs:84:35 | LL | unsafe fn also_broken(x: &u32) -> &mut u32 { | ^^^^^^^^ | note: immutable borrow here - --> tests/ui/mut_from_ref.rs:85:26 + --> tests/ui/mut_from_ref.rs:84:26 | LL | unsafe fn also_broken(x: &u32) -> &mut u32 { | ^^^^ diff --git a/src/tools/clippy/tests/ui/mut_mut.fixed b/src/tools/clippy/tests/ui/mut_mut.fixed index f9a7f5dcb5a15..c34f605e3da6e 100644 --- a/src/tools/clippy/tests/ui/mut_mut.fixed +++ b/src/tools/clippy/tests/ui/mut_mut.fixed @@ -1,13 +1,6 @@ //@aux-build:proc_macros.rs #![warn(clippy::mut_mut)] -#![allow(unused)] -#![allow( - clippy::no_effect, - clippy::uninlined_format_args, - clippy::unnecessary_operation, - clippy::needless_pass_by_ref_mut -)] extern crate proc_macros; use proc_macros::{external, inline_macros}; @@ -26,7 +19,6 @@ macro_rules! mut_ptr { }; } -#[allow(unused_mut, unused_variables)] #[inline_macros] fn main() { let mut x = &mut 1u32; diff --git a/src/tools/clippy/tests/ui/mut_mut.rs b/src/tools/clippy/tests/ui/mut_mut.rs index bbec48011a17e..d17394dfe3f69 100644 --- a/src/tools/clippy/tests/ui/mut_mut.rs +++ b/src/tools/clippy/tests/ui/mut_mut.rs @@ -1,13 +1,6 @@ //@aux-build:proc_macros.rs #![warn(clippy::mut_mut)] -#![allow(unused)] -#![allow( - clippy::no_effect, - clippy::uninlined_format_args, - clippy::unnecessary_operation, - clippy::needless_pass_by_ref_mut -)] extern crate proc_macros; use proc_macros::{external, inline_macros}; @@ -26,7 +19,6 @@ macro_rules! mut_ptr { }; } -#[allow(unused_mut, unused_variables)] #[inline_macros] fn main() { let mut x = &mut &mut 1u32; diff --git a/src/tools/clippy/tests/ui/mut_mut.stderr b/src/tools/clippy/tests/ui/mut_mut.stderr index 85e9c5649b89a..0a7a923e0f48f 100644 --- a/src/tools/clippy/tests/ui/mut_mut.stderr +++ b/src/tools/clippy/tests/ui/mut_mut.stderr @@ -1,5 +1,5 @@ error: a type of form `&mut &mut _` - --> tests/ui/mut_mut.rs:15:11 + --> tests/ui/mut_mut.rs:8:11 | LL | fn fun(x: &mut &mut u32) { | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` @@ -8,37 +8,37 @@ LL | fn fun(x: &mut &mut u32) { = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut.rs:32:17 + --> tests/ui/mut_mut.rs:24:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` error: this expression mutably borrows a mutable reference - --> tests/ui/mut_mut.rs:35:21 + --> tests/ui/mut_mut.rs:27:21 | LL | let mut y = &mut x; | ^^^^^^ help: reborrow instead: `&mut *x` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut.rs:40:32 + --> tests/ui/mut_mut.rs:32:32 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` error: a type of form `&mut &mut _` - --> tests/ui/mut_mut.rs:40:16 + --> tests/ui/mut_mut.rs:32:16 | LL | let y: &mut &mut u32 = &mut &mut 2; | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut.rs:46:37 + --> tests/ui/mut_mut.rs:38:37 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` error: a type of form `&mut &mut _` - --> tests/ui/mut_mut.rs:46:16 + --> tests/ui/mut_mut.rs:38:16 | LL | let y: &mut &mut &mut u32 = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut u32` diff --git a/src/tools/clippy/tests/ui/mut_mut_unfixable.rs b/src/tools/clippy/tests/ui/mut_mut_unfixable.rs index 271cb7b968898..0c6444fd9fa35 100644 --- a/src/tools/clippy/tests/ui/mut_mut_unfixable.rs +++ b/src/tools/clippy/tests/ui/mut_mut_unfixable.rs @@ -1,7 +1,6 @@ //@no-rustfix #![warn(clippy::mut_mut)] -#![allow(unused)] #![expect(clippy::no_effect)] //! removing the extra `&mut`s will break the derefs diff --git a/src/tools/clippy/tests/ui/mut_mut_unfixable.stderr b/src/tools/clippy/tests/ui/mut_mut_unfixable.stderr index cf66eb2ed1eca..7e7fb801ce1e7 100644 --- a/src/tools/clippy/tests/ui/mut_mut_unfixable.stderr +++ b/src/tools/clippy/tests/ui/mut_mut_unfixable.stderr @@ -1,5 +1,5 @@ error: a type of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:9:11 + --> tests/ui/mut_mut_unfixable.rs:8:11 | LL | fn fun(x: &mut &mut u32) -> bool { | ^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut u32` @@ -8,31 +8,31 @@ LL | fn fun(x: &mut &mut u32) -> bool { = help: to override `-D warnings` add `#[allow(clippy::mut_mut)]` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:15:17 + --> tests/ui/mut_mut_unfixable.rs:14:17 | LL | let mut x = &mut &mut 1u32; | ^^^^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 1u32` error: this expression mutably borrows a mutable reference - --> tests/ui/mut_mut_unfixable.rs:18:21 + --> tests/ui/mut_mut_unfixable.rs:17:21 | LL | let mut y = &mut x; | ^^^^^^ help: reborrow instead: `&mut *x` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:24:17 + --> tests/ui/mut_mut_unfixable.rs:23:17 | LL | let y = &mut &mut 2; | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut 2` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:30:17 + --> tests/ui/mut_mut_unfixable.rs:29:17 | LL | let y = &mut &mut &mut 2; | ^^^^^^^^^^^^^^^^ help: remove the extra `&mut`s: `&mut 2` error: an expression of form `&mut &mut _` - --> tests/ui/mut_mut_unfixable.rs:39:17 + --> tests/ui/mut_mut_unfixable.rs:38:17 | LL | let y = &mut &mut x; | ^^^^^^^^^^^ help: remove the extra `&mut`: `&mut x` diff --git a/src/tools/clippy/tests/ui/mut_mutex_lock.fixed b/src/tools/clippy/tests/ui/mut_mutex_lock.fixed index 5790ee8c1b022..44587b89ec836 100644 --- a/src/tools/clippy/tests/ui/mut_mutex_lock.fixed +++ b/src/tools/clippy/tests/ui/mut_mutex_lock.fixed @@ -1,4 +1,3 @@ -#![allow(dead_code, unused_mut)] #![warn(clippy::mut_mutex_lock)] use std::sync::{Arc, Mutex}; diff --git a/src/tools/clippy/tests/ui/mut_mutex_lock.rs b/src/tools/clippy/tests/ui/mut_mutex_lock.rs index 7286afac823b7..eef4f26c09595 100644 --- a/src/tools/clippy/tests/ui/mut_mutex_lock.rs +++ b/src/tools/clippy/tests/ui/mut_mutex_lock.rs @@ -1,4 +1,3 @@ -#![allow(dead_code, unused_mut)] #![warn(clippy::mut_mutex_lock)] use std::sync::{Arc, Mutex}; diff --git a/src/tools/clippy/tests/ui/mut_mutex_lock.stderr b/src/tools/clippy/tests/ui/mut_mutex_lock.stderr index 67543c73c6eef..587405449523c 100644 --- a/src/tools/clippy/tests/ui/mut_mutex_lock.stderr +++ b/src/tools/clippy/tests/ui/mut_mutex_lock.stderr @@ -1,5 +1,5 @@ error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference - --> tests/ui/mut_mutex_lock.rs:10:33 + --> tests/ui/mut_mutex_lock.rs:9:33 | LL | let mut value = value_mutex.lock().unwrap(); | ^^^^ help: change this to: `get_mut` @@ -8,7 +8,7 @@ LL | let mut value = value_mutex.lock().unwrap(); = help: to override `-D warnings` add `#[allow(clippy::mut_mutex_lock)]` error: calling `&mut Mutex::lock` unnecessarily locks an exclusive (mutable) reference - --> tests/ui/mut_mutex_lock.rs:16:43 + --> tests/ui/mut_mutex_lock.rs:15:43 | LL | let mut value = mut_ref_mut_ref_mutex.lock().unwrap(); | ^^^^ help: change this to: `get_mut` diff --git a/src/tools/clippy/tests/ui/mut_range_bound.rs b/src/tools/clippy/tests/ui/mut_range_bound.rs index 107a6229b86d8..4e1192a86c2c1 100644 --- a/src/tools/clippy/tests/ui/mut_range_bound.rs +++ b/src/tools/clippy/tests/ui/mut_range_bound.rs @@ -1,4 +1,4 @@ -#![allow(unused)] +#![warn(clippy::mut_range_bound)] fn main() {} diff --git a/src/tools/clippy/tests/ui/mutex_atomic.fixed b/src/tools/clippy/tests/ui/mutex_atomic.fixed index dc05d8a2c61fe..711aba8da37b6 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.fixed +++ b/src/tools/clippy/tests/ui/mutex_atomic.fixed @@ -1,6 +1,5 @@ -#![warn(clippy::mutex_integer)] -#![warn(clippy::mutex_atomic)] -#![allow(clippy::borrow_as_ptr)] +#![warn(clippy::mutex_atomic, clippy::mutex_integer)] +#![expect(clippy::borrow_as_ptr)] use std::sync::Mutex; diff --git a/src/tools/clippy/tests/ui/mutex_atomic.rs b/src/tools/clippy/tests/ui/mutex_atomic.rs index 33745f8fc5e13..f0ca75458d100 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.rs +++ b/src/tools/clippy/tests/ui/mutex_atomic.rs @@ -1,6 +1,5 @@ -#![warn(clippy::mutex_integer)] -#![warn(clippy::mutex_atomic)] -#![allow(clippy::borrow_as_ptr)] +#![warn(clippy::mutex_atomic, clippy::mutex_integer)] +#![expect(clippy::borrow_as_ptr)] use std::sync::Mutex; diff --git a/src/tools/clippy/tests/ui/mutex_atomic.stderr b/src/tools/clippy/tests/ui/mutex_atomic.stderr index c1391e0111c8d..85c1829f44121 100644 --- a/src/tools/clippy/tests/ui/mutex_atomic.stderr +++ b/src/tools/clippy/tests/ui/mutex_atomic.stderr @@ -1,5 +1,5 @@ error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:8:13 + --> tests/ui/mutex_atomic.rs:7:13 | LL | let _ = Mutex::new(true); | ^^^^^^^^^^^^^^^^ @@ -14,7 +14,7 @@ LL + let _ = std::sync::atomic::AtomicBool::new(true); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:11:13 + --> tests/ui/mutex_atomic.rs:10:13 | LL | let _ = Mutex::new(5usize); | ^^^^^^^^^^^^^^^^^^ @@ -27,7 +27,7 @@ LL + let _ = std::sync::atomic::AtomicUsize::new(5usize); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:14:13 + --> tests/ui/mutex_atomic.rs:13:13 | LL | let _ = Mutex::new(9isize); | ^^^^^^^^^^^^^^^^^^ @@ -40,7 +40,7 @@ LL + let _ = std::sync::atomic::AtomicIsize::new(9isize); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:21:13 + --> tests/ui/mutex_atomic.rs:20:13 | LL | let _ = Mutex::new(&mut x as *mut u32); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -53,7 +53,7 @@ LL + let _ = std::sync::atomic::AtomicPtr::new(&mut x as *mut u32); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:24:13 + --> tests/ui/mutex_atomic.rs:23:13 | LL | let _ = Mutex::new(0u32); | ^^^^^^^^^^^^^^^^ @@ -68,7 +68,7 @@ LL + let _ = std::sync::atomic::AtomicU32::new(0u32); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:27:13 + --> tests/ui/mutex_atomic.rs:26:13 | LL | let _ = Mutex::new(0i32); | ^^^^^^^^^^^^^^^^ @@ -81,7 +81,7 @@ LL + let _ = std::sync::atomic::AtomicI32::new(0i32); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:31:13 + --> tests/ui/mutex_atomic.rs:30:13 | LL | let _ = Mutex::new(0u8); | ^^^^^^^^^^^^^^^ @@ -94,7 +94,7 @@ LL + let _ = std::sync::atomic::AtomicU8::new(0u8); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:34:13 + --> tests/ui/mutex_atomic.rs:33:13 | LL | let _ = Mutex::new(0i16); | ^^^^^^^^^^^^^^^^ @@ -107,7 +107,7 @@ LL + let _ = std::sync::atomic::AtomicI16::new(0i16); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:37:25 + --> tests/ui/mutex_atomic.rs:36:25 | LL | let _x: Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -120,7 +120,7 @@ LL + let _x = std::sync::atomic::AtomicI8::new(0); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:41:13 + --> tests/ui/mutex_atomic.rs:40:13 | LL | let _ = Mutex::new(X); | ^^^^^^^^^^^^^ @@ -133,7 +133,7 @@ LL + let _ = std::sync::atomic::AtomicI64::new(X); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:53:30 + --> tests/ui/mutex_atomic.rs:52:30 | LL | static MTX: Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -146,7 +146,7 @@ LL + static MTX: std::sync::atomic::AtomicU32 = std::sync::atomic::AtomicU32 | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:56:15 + --> tests/ui/mutex_atomic.rs:55:15 | LL | let mtx = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -159,7 +159,7 @@ LL + let mtx = std::sync::atomic::AtomicI32::new(0); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:60:22 + --> tests/ui/mutex_atomic.rs:59:22 | LL | let reassigned = mtx; | ^^^ @@ -168,7 +168,7 @@ LL | let reassigned = mtx; = help: if you just want the locking behavior and not the internal type, consider using `Mutex<()>` error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:65:35 + --> tests/ui/mutex_atomic.rs:64:35 | LL | let (funky_mtx): Mutex = Mutex::new(0); | ^^^^^^^^^^^^^ @@ -181,7 +181,7 @@ LL + let (funky_mtx) = std::sync::atomic::AtomicU64::new(0); | error: using a `Mutex` where an atomic would do - --> tests/ui/mutex_atomic.rs:76:13 + --> tests/ui/mutex_atomic.rs:75:13 | LL | let _ = Mutex::new(test_expr!(1)); | ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed index adb9096c9f242..0eef094895dd8 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.fixed @@ -1,5 +1,5 @@ #![warn(clippy::needless_arbitrary_self_type)] -#![allow(unused_mut, clippy::needless_lifetimes)] +#![expect(clippy::needless_lifetimes)] pub enum ValType { A, diff --git a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs index 550546ed24fd7..0ea16ce89946b 100644 --- a/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs +++ b/src/tools/clippy/tests/ui/needless_arbitrary_self_type.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_arbitrary_self_type)] -#![allow(unused_mut, clippy::needless_lifetimes)] +#![expect(clippy::needless_lifetimes)] pub enum ValType { A, diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed b/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed index eedeecd8a8cbc..a5c34939a5c9d 100644 --- a/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed +++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.fixed @@ -1,5 +1,5 @@ #![warn(clippy::needless_bitwise_bool)] -#![allow(clippy::const_is_empty)] +#![expect(clippy::const_is_empty)] fn returns_bool() -> bool { true diff --git a/src/tools/clippy/tests/ui/needless_bitwise_bool.rs b/src/tools/clippy/tests/ui/needless_bitwise_bool.rs index 5c0a4ccf266c3..ca03d888f3650 100644 --- a/src/tools/clippy/tests/ui/needless_bitwise_bool.rs +++ b/src/tools/clippy/tests/ui/needless_bitwise_bool.rs @@ -1,5 +1,5 @@ #![warn(clippy::needless_bitwise_bool)] -#![allow(clippy::const_is_empty)] +#![expect(clippy::const_is_empty)] fn returns_bool() -> bool { true diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed index 0af4f87bec677..c3eed4e3fd717 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.fixed +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.fixed @@ -1,15 +1,6 @@ #![warn(clippy::needless_bool)] -#![allow( - unused, - dead_code, - clippy::no_effect, - clippy::if_same_then_else, - clippy::equatable_if_let, - clippy::needless_ifs, - clippy::needless_return, - clippy::self_named_constructors, - clippy::struct_field_names -)] +#![allow(clippy::no_effect)] +#![expect(clippy::needless_return)] use std::cell::Cell; @@ -21,7 +12,6 @@ macro_rules! bool_comparison_trigger { $($i: (Cell, bool, bool)),+ } - #[allow(dead_code)] impl Trigger { pub fn trigger(&self, key: &str) -> bool { $( diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.rs b/src/tools/clippy/tests/ui/needless_bool/fixable.rs index 2e8719bd041e0..4571628be20ea 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.rs +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.rs @@ -1,15 +1,6 @@ #![warn(clippy::needless_bool)] -#![allow( - unused, - dead_code, - clippy::no_effect, - clippy::if_same_then_else, - clippy::equatable_if_let, - clippy::needless_ifs, - clippy::needless_return, - clippy::self_named_constructors, - clippy::struct_field_names -)] +#![allow(clippy::no_effect)] +#![expect(clippy::needless_return)] use std::cell::Cell; @@ -21,7 +12,6 @@ macro_rules! bool_comparison_trigger { $($i: (Cell, bool, bool)),+ } - #[allow(dead_code)] impl Trigger { pub fn trigger(&self, key: &str) -> bool { $( diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr index e15260242f4dd..f8cad52c2bbf2 100644 --- a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr +++ b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:41:5 + --> tests/ui/needless_bool/fixable.rs:31:5 | LL | / if x { LL | | true @@ -12,7 +12,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::needless_bool)]` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:47:5 + --> tests/ui/needless_bool/fixable.rs:37:5 | LL | / if x { LL | | false @@ -22,7 +22,7 @@ LL | | }; | |_____^ help: you can reduce it to: `!x` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:53:5 + --> tests/ui/needless_bool/fixable.rs:43:5 | LL | / if x && y { LL | | false @@ -32,7 +32,7 @@ LL | | }; | |_____^ help: you can reduce it to: `!(x && y)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:62:5 + --> tests/ui/needless_bool/fixable.rs:52:5 | LL | / if a == b { LL | | false @@ -42,7 +42,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a != b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:68:5 + --> tests/ui/needless_bool/fixable.rs:58:5 | LL | / if a != b { LL | | false @@ -52,7 +52,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a == b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:74:5 + --> tests/ui/needless_bool/fixable.rs:64:5 | LL | / if a < b { LL | | false @@ -62,7 +62,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a >= b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:80:5 + --> tests/ui/needless_bool/fixable.rs:70:5 | LL | / if a <= b { LL | | false @@ -72,7 +72,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a > b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:86:5 + --> tests/ui/needless_bool/fixable.rs:76:5 | LL | / if a > b { LL | | false @@ -82,7 +82,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a <= b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:92:5 + --> tests/ui/needless_bool/fixable.rs:82:5 | LL | / if a >= b { LL | | false @@ -92,7 +92,7 @@ LL | | }; | |_____^ help: you can reduce it to: `a < b` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:121:5 + --> tests/ui/needless_bool/fixable.rs:111:5 | LL | / if x { LL | | return true; @@ -102,7 +102,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:130:5 + --> tests/ui/needless_bool/fixable.rs:120:5 | LL | / if x { LL | | return false; @@ -112,7 +112,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !x` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:139:5 + --> tests/ui/needless_bool/fixable.rs:129:5 | LL | / if x && y { LL | | return true; @@ -122,7 +122,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return x && y` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:148:5 + --> tests/ui/needless_bool/fixable.rs:138:5 | LL | / if x && y { LL | | return false; @@ -132,7 +132,7 @@ LL | | }; | |_____^ help: you can reduce it to: `return !(x && y)` error: equality checks against true are unnecessary - --> tests/ui/needless_bool/fixable.rs:157:8 + --> tests/ui/needless_bool/fixable.rs:147:8 | LL | if x == true {}; | ^^^^^^^^^ help: try: `x` @@ -141,25 +141,25 @@ LL | if x == true {}; = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]` error: equality checks against false can be replaced by a negation - --> tests/ui/needless_bool/fixable.rs:162:8 + --> tests/ui/needless_bool/fixable.rs:152:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try: `!x` error: equality checks against true are unnecessary - --> tests/ui/needless_bool/fixable.rs:173:8 + --> tests/ui/needless_bool/fixable.rs:163:8 | LL | if x == true {}; | ^^^^^^^^^ help: try: `x` error: equality checks against false can be replaced by a negation - --> tests/ui/needless_bool/fixable.rs:175:8 + --> tests/ui/needless_bool/fixable.rs:165:8 | LL | if x == false {}; | ^^^^^^^^^^ help: try: `!x` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:185:12 + --> tests/ui/needless_bool/fixable.rs:175:12 | LL | } else if returns_bool() { | ____________^ @@ -170,7 +170,7 @@ LL | | }; | |_____^ help: you can reduce it to: `{ !returns_bool() }` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:199:5 + --> tests/ui/needless_bool/fixable.rs:189:5 | LL | / if unsafe { no(4) } & 1 != 0 { LL | | true @@ -180,37 +180,37 @@ LL | | }; | |_____^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:205:30 + --> tests/ui/needless_bool/fixable.rs:195:30 | LL | let _brackets_unneeded = if unsafe { no(4) } & 1 != 0 { true } else { false }; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `unsafe { no(4) } & 1 != 0` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:209:9 + --> tests/ui/needless_bool/fixable.rs:199:9 | LL | if unsafe { no(4) } & 1 != 0 { true } else { false } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(unsafe { no(4) } & 1 != 0)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:221:14 + --> tests/ui/needless_bool/fixable.rs:211:14 | LL | let _x = if a && b { true } else { false }.then(|| todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(a && b)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:223:14 + --> tests/ui/needless_bool/fixable.rs:213:14 | LL | let _x = if a && b { true } else { false } as u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `(a && b)` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:227:14 + --> tests/ui/needless_bool/fixable.rs:217:14 | LL | let _x = if a { true } else { false }.then(|| todo!()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: you can reduce it to: `a` error: this if-then-else expression returns a bool literal - --> tests/ui/needless_bool/fixable.rs:239:5 + --> tests/ui/needless_bool/fixable.rs:229:5 | LL | / if test_expr!(x) { LL | | true diff --git a/src/tools/clippy/tests/ui/needless_bool/simple.rs b/src/tools/clippy/tests/ui/needless_bool/simple.rs index 40ffeae6c56fa..c5d2ef51fade5 100644 --- a/src/tools/clippy/tests/ui/needless_bool/simple.rs +++ b/src/tools/clippy/tests/ui/needless_bool/simple.rs @@ -1,12 +1,5 @@ #![warn(clippy::needless_bool)] -#![allow( - unused, - dead_code, - clippy::no_effect, - clippy::if_same_then_else, - clippy::needless_return, - clippy::branches_sharing_code -)] +#![expect(clippy::if_same_then_else, clippy::needless_return)] fn main() { let x = true; diff --git a/src/tools/clippy/tests/ui/needless_bool/simple.stderr b/src/tools/clippy/tests/ui/needless_bool/simple.stderr index 077e9b5df47cf..07711b9234cdd 100644 --- a/src/tools/clippy/tests/ui/needless_bool/simple.stderr +++ b/src/tools/clippy/tests/ui/needless_bool/simple.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression will always return true - --> tests/ui/needless_bool/simple.rs:14:5 + --> tests/ui/needless_bool/simple.rs:7:5 | LL | / if x { LL | | true @@ -12,7 +12,7 @@ LL | | }; = help: to override `-D warnings` add `#[allow(clippy::needless_bool)]` error: this if-then-else expression will always return false - --> tests/ui/needless_bool/simple.rs:20:5 + --> tests/ui/needless_bool/simple.rs:13:5 | LL | / if x { LL | | false @@ -22,7 +22,7 @@ LL | | }; | |_____^ error: this if-then-else expression will always return true - --> tests/ui/needless_bool/simple.rs:36:5 + --> tests/ui/needless_bool/simple.rs:29:5 | LL | / if x { LL | | return true; @@ -32,7 +32,7 @@ LL | | }; | |_____^ error: this if-then-else expression will always return false - --> tests/ui/needless_bool/simple.rs:45:5 + --> tests/ui/needless_bool/simple.rs:38:5 | LL | / if x { LL | | return false; diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.fixed b/src/tools/clippy/tests/ui/needless_bool_assign.fixed index 8fd5720381404..3bd592b471e4b 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.fixed +++ b/src/tools/clippy/tests/ui/needless_bool_assign.fixed @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::needless_bool_assign)] fn random() -> bool { diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.rs b/src/tools/clippy/tests/ui/needless_bool_assign.rs index 4721ab433b327..1279176cb20a2 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.rs +++ b/src/tools/clippy/tests/ui/needless_bool_assign.rs @@ -1,4 +1,3 @@ -#![allow(unused)] #![warn(clippy::needless_bool_assign)] fn random() -> bool { diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.stderr b/src/tools/clippy/tests/ui/needless_bool_assign.stderr index 34ff782f34a35..3dfea7cd1af99 100644 --- a/src/tools/clippy/tests/ui/needless_bool_assign.stderr +++ b/src/tools/clippy/tests/ui/needless_bool_assign.stderr @@ -1,5 +1,5 @@ error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:13:5 + --> tests/ui/needless_bool_assign.rs:12:5 | LL | / if random() && random() { LL | | a.field = true; @@ -12,7 +12,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::needless_bool_assign)]` error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:19:5 + --> tests/ui/needless_bool_assign.rs:18:5 | LL | / if random() && random() { LL | | a.field = false; @@ -22,7 +22,7 @@ LL | | } | |_____^ help: you can reduce it to: `a.field = !(random() && random());` error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:34:5 + --> tests/ui/needless_bool_assign.rs:33:5 | LL | / if random() { LL | | a.field = true; @@ -32,7 +32,7 @@ LL | | } | |_____^ help: you can reduce it to: `random(); a.field = true;` error: this `if` has identical blocks - --> tests/ui/needless_bool_assign.rs:34:17 + --> tests/ui/needless_bool_assign.rs:33:17 | LL | if random() { | _________________^ @@ -41,7 +41,7 @@ LL | | } else { | |_____^ | note: same as this - --> tests/ui/needless_bool_assign.rs:36:12 + --> tests/ui/needless_bool_assign.rs:35:12 | LL | } else { | ____________^ @@ -52,7 +52,7 @@ LL | | } = help: to override `-D warnings` add `#[allow(clippy::if_same_then_else)]` error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:54:12 + --> tests/ui/needless_bool_assign.rs:53:12 | LL | } else if x || y { | ____________^ @@ -63,7 +63,7 @@ LL | | } | |_____^ help: you can reduce it to: `{ z = x || y; }` error: this if-then-else expression assigns a bool literal - --> tests/ui/needless_bool_assign.rs:77:5 + --> tests/ui/needless_bool_assign.rs:76:5 | LL | / if invoke!(must_keep, x, y) { LL | | dot_0!(skip) = false; diff --git a/src/tools/clippy/tests/ui/needless_late_init.fixed b/src/tools/clippy/tests/ui/needless_late_init.fixed index b686a8e9f1a06..a5c9d435b3eea 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.fixed +++ b/src/tools/clippy/tests/ui/needless_late_init.fixed @@ -185,25 +185,24 @@ fn does_not_lint() { }; // using tuples would be possible, but not always preferable - let x; - let y; - if true { - x = 1; - y = 2; + + + let (x, y) = if true { + //~^ needless_late_init + (1, 2) } else { - x = 3; - y = 4; - } + (3, 4) + }; - // could match with a smarter heuristic to avoid multiple assignments - let x; - if true { + + //~^ needless_late_init + let x = if true { let mut y = 5; y = 6; - x = y; + y } else { - x = 2; - } + 2 + }; let (x, y); if true { @@ -299,3 +298,167 @@ fn issue9895() { //~^ needless_late_init let r = 5; } + +fn if_or_match_in_block_expr() { + + //~^ needless_late_init + let z = if true { + 1 + } else { + 2 + }; +} + +fn issue16330() { + // Late init in both if branches, should lint + + + let (a, b) = if true { + //~^ needless_late_init + (1, 2) + } else { + (3, 4) + }; + + // One of the variables is not late init, should not lint + let a; + let mut b = 1; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + + // One of the variables is defined outside the block, should not lint + let b; + { + let a; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + } + + // Late init in all match arms, should lint + + + + let (a, b, c) = match 1 { + //~^ needless_late_init + 1 => { + (1, 2, 3) + }, + _ if false => { + (4, 5, 6) + }, + _ => { + (7, 8, 9) + }, + }; + + // Late init in all if branches, should lint + + + + let (a, b, c) = if true { + //~^ needless_late_init + (1, 2, 3) + } else if false { + (4, 5, 6) + } else { + (7, 8, 9) + }; + + // One of the variables is not assigned in all branches, should not lint + let a; + let b; + let c; + if true { + a = 1; + b = 2; + c = 3; + } else if false { + a = 4; + c = 6; + } else { + a = 7; + b = 8; + c = 9; + } + + // One of the variables is assigned multiple times, should not lint + let mut a; + let b; + if true { + a = 1; + b = 2; + a = 3; + } else { + a = 4; + b = 5; + } + + // One of the variables is assigned in a nested block, should not lint + let a; + let b; + if true { + a = 1; + b = 2; + } else { + a = 4; + { + b = 5; + } + } + + // The order of the variables is different in different branches, should not lint + let a; + let b; + if true { + a = 1; + b = 2; + } else { + b = 5; + a = 4; + } + + // Later assignments depend on the earlier ones, should only lint the last ones + let a; + let b; + + //~^ needless_late_init + let c = if true { + a = 1; + b = a + 1; + b + 1 + } else { + a = 4; + b = a + 2; + b + 2 + }; + let a; + let b; + + + let (c, d) = if true { + //~^ needless_late_init + a = 1; + b = a + 1; + (b + 1, 1) + } else { + a = 4; + b = a + 2; + (b + 2, 2) + }; +} diff --git a/src/tools/clippy/tests/ui/needless_late_init.rs b/src/tools/clippy/tests/ui/needless_late_init.rs index 23772ff702931..ee413814750c7 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.rs +++ b/src/tools/clippy/tests/ui/needless_late_init.rs @@ -188,6 +188,7 @@ fn does_not_lint() { let x; let y; if true { + //~^ needless_late_init x = 1; y = 2; } else { @@ -195,8 +196,8 @@ fn does_not_lint() { y = 4; } - // could match with a smarter heuristic to avoid multiple assignments let x; + //~^ needless_late_init if true { let mut y = 5; y = 6; @@ -299,3 +300,183 @@ fn issue9895() { //~^ needless_late_init (r = 5); } + +fn if_or_match_in_block_expr() { + let z; + //~^ needless_late_init + if true { + z = 1; + } else { + z = 2; + } +} + +fn issue16330() { + // Late init in both if branches, should lint + let a; + let b; + if true { + //~^ needless_late_init + a = 1; + b = 2; + } else { + a = 3; + b = 4; + } + + // One of the variables is not late init, should not lint + let a; + let mut b = 1; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + + // One of the variables is defined outside the block, should not lint + let b; + { + let a; + let c; + if true { + b = 1; + a = 2; + c = 3; + } else { + b = 6; + a = 4; + c = 5; + } + } + + // Late init in all match arms, should lint + let a; + let b; + let c; + match 1 { + //~^ needless_late_init + 1 => { + a = 1; + b = 2; + c = 3; + }, + _ if false => { + a = 4; + b = 5; + c = 6; + }, + _ => { + a = 7; + b = 8; + c = 9; + }, + } + + // Late init in all if branches, should lint + let a; + let b; + let c; + if true { + //~^ needless_late_init + a = 1; + b = 2; + c = 3; + } else if false { + a = 4; + b = 5; + c = 6; + } else { + a = 7; + b = 8; + c = 9; + } + + // One of the variables is not assigned in all branches, should not lint + let a; + let b; + let c; + if true { + a = 1; + b = 2; + c = 3; + } else if false { + a = 4; + c = 6; + } else { + a = 7; + b = 8; + c = 9; + } + + // One of the variables is assigned multiple times, should not lint + let mut a; + let b; + if true { + a = 1; + b = 2; + a = 3; + } else { + a = 4; + b = 5; + } + + // One of the variables is assigned in a nested block, should not lint + let a; + let b; + if true { + a = 1; + b = 2; + } else { + a = 4; + { + b = 5; + } + } + + // The order of the variables is different in different branches, should not lint + let a; + let b; + if true { + a = 1; + b = 2; + } else { + b = 5; + a = 4; + } + + // Later assignments depend on the earlier ones, should only lint the last ones + let a; + let b; + let c; + //~^ needless_late_init + if true { + a = 1; + b = a + 1; + c = b + 1; + } else { + a = 4; + b = a + 2; + c = b + 2; + } + let a; + let b; + let c; + let d; + if true { + //~^ needless_late_init + a = 1; + b = a + 1; + c = b + 1; + d = 1; + } else { + a = 4; + b = a + 2; + c = b + 2; + d = 2; + } +} diff --git a/src/tools/clippy/tests/ui/needless_late_init.stderr b/src/tools/clippy/tests/ui/needless_late_init.stderr index e3e25cdc8d769..12bfc029d9488 100644 --- a/src/tools/clippy/tests/ui/needless_late_init.stderr +++ b/src/tools/clippy/tests/ui/needless_late_init.stderr @@ -276,7 +276,50 @@ LL ~ }; | error: unneeded late initialization - --> tests/ui/needless_late_init.rs:298:5 + --> tests/ui/needless_late_init.rs:199:5 + | +LL | let x; + | ^^^^^^ + | +help: move the declaration `x` here and remove the assignments from the branches + | +LL ~ +LL | +LL ~ let x = if true { +LL | let mut y = 5; +LL | y = 6; +LL ~ y +LL | } else { +LL ~ 2 +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:190:5 + | +LL | / if true { +LL | | +LL | | x = 1; +LL | | y = 2; +... | +LL | | y = 4; +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the branches + | +LL ~ +LL ~ +LL ~ let (x, y) = if true { +LL | +LL ~ (1, 2) +LL | } else { +LL ~ (3, 4) +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:299:5 | LL | let r; | ^^^^^^ created here @@ -291,5 +334,153 @@ LL | LL ~ let r = 5; | -error: aborting due to 17 previous errors +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:305:5 + | +LL | let z; + | ^^^^^^ + | +help: move the declaration `z` here and remove the assignments from the branches + | +LL ~ +LL | +LL ~ let z = if true { +LL ~ 1 +LL | } else { +LL ~ 2 +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:455:5 + | +LL | let c; + | ^^^^^^ + | +help: move the declaration `c` here and remove the assignments from the branches + | +LL ~ +LL | +LL ~ let c = if true { +LL | a = 1; +LL | b = a + 1; +LL ~ b + 1 +LL | } else { +LL | a = 4; +LL | b = a + 2; +LL ~ b + 2 +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:318:5 + | +LL | / if true { +LL | | +LL | | a = 1; +LL | | b = 2; +... | +LL | | b = 4; +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the branches + | +LL ~ +LL ~ +LL ~ let (a, b) = if true { +LL | +LL ~ (1, 2) +LL | } else { +LL ~ (3, 4) +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:361:5 + | +LL | / match 1 { +LL | | +LL | | 1 => { +LL | | a = 1; +... | +LL | | }, +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the `match` arms + | +LL ~ +LL ~ +LL ~ +LL ~ let (a, b, c) = match 1 { +LL | +LL | 1 => { +LL ~ (1, 2, 3) +LL | }, +LL | _ if false => { +LL ~ (4, 5, 6) +LL | }, +LL | _ => { +LL ~ (7, 8, 9) +LL | }, +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:384:5 + | +LL | / if true { +LL | | +LL | | a = 1; +LL | | b = 2; +... | +LL | | c = 9; +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the branches + | +LL ~ +LL ~ +LL ~ +LL ~ let (a, b, c) = if true { +LL | +LL ~ (1, 2, 3) +LL | } else if false { +LL ~ (4, 5, 6) +LL | } else { +LL ~ (7, 8, 9) +LL ~ }; + | + +error: unneeded late initialization + --> tests/ui/needless_late_init.rs:470:5 + | +LL | / if true { +LL | | +LL | | a = 1; +LL | | b = a + 1; +... | +LL | | d = 2; +LL | | } + | |_____^ + | +help: move the declarations here and remove the assignments from the branches + | +LL ~ +LL ~ +LL ~ let (c, d) = if true { +LL | +LL | a = 1; +LL | b = a + 1; +LL ~ (b + 1, 1) +LL | } else { +LL | a = 4; +LL | b = a + 2; +LL ~ (b + 2, 2) +LL ~ }; + | + +error: aborting due to 25 previous errors diff --git a/src/tools/clippy/tests/ui/ref_patterns.rs b/src/tools/clippy/tests/ui/ref_patterns.rs index 2905af13ccb63..3628cc3d537c0 100644 --- a/src/tools/clippy/tests/ui/ref_patterns.rs +++ b/src/tools/clippy/tests/ui/ref_patterns.rs @@ -19,4 +19,14 @@ fn use_in_binding() { fn use_in_parameter(ref x: i32) {} //~^ ref_patterns +struct Foo {} + +// shouldn't trigger the lint +#[automatically_derived] +impl Foo { + fn foo() { + if let Some(ref x) = Some(1) {} + } +} + fn main() {} diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed index c27cec558242d..3020ed5c6f39d 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed +++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed @@ -96,3 +96,23 @@ fn issue15579() { let layout = alloc::Layout::new::(); } + +#[warn(clippy::std_instead_of_core)] +fn issue13158_core_io() { + // items moved from std::io into core::io are stable in an unstable module. + use std::io::ErrorKind; +} + +#[clippy::msrv = "1.40"] +fn issue13158_msrv_1_40(_: &dyn std::panic::UnwindSafe) {} + +#[clippy::msrv = "1.41"] +fn issue13158_msrv_1_41(_: &dyn core::panic::UnwindSafe) {} +//~^ std_instead_of_core + +#[clippy::msrv = "1.80"] +fn issue13158_msrv_1_80(_: &dyn std::error::Error) {} + +#[clippy::msrv = "1.81"] +fn issue13158_msrv_1_81(_: &dyn core::error::Error) {} +//~^ std_instead_of_core diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs index 7d53f7fc3072b..49b4218aa8983 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.rs +++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs @@ -96,3 +96,23 @@ fn issue15579() { let layout = alloc::Layout::new::(); } + +#[warn(clippy::std_instead_of_core)] +fn issue13158_core_io() { + // items moved from std::io into core::io are stable in an unstable module. + use std::io::ErrorKind; +} + +#[clippy::msrv = "1.40"] +fn issue13158_msrv_1_40(_: &dyn std::panic::UnwindSafe) {} + +#[clippy::msrv = "1.41"] +fn issue13158_msrv_1_41(_: &dyn std::panic::UnwindSafe) {} +//~^ std_instead_of_core + +#[clippy::msrv = "1.80"] +fn issue13158_msrv_1_80(_: &dyn std::error::Error) {} + +#[clippy::msrv = "1.81"] +fn issue13158_msrv_1_81(_: &dyn std::error::Error) {} +//~^ std_instead_of_core diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.stderr b/src/tools/clippy/tests/ui/std_instead_of_core.stderr index a5f8fbbe37cb8..0363852cf8d49 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core.stderr +++ b/src/tools/clippy/tests/ui/std_instead_of_core.stderr @@ -97,5 +97,17 @@ error: used import from `std` instead of `core` LL | fn msrv_1_77(_: std::net::IpAddr) {} | ^^^ help: consider importing the item from `core`: `core` -error: aborting due to 15 previous errors +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core.rs:110:33 + | +LL | fn issue13158_msrv_1_41(_: &dyn std::panic::UnwindSafe) {} + | ^^^ help: consider importing the item from `core`: `core` + +error: used import from `std` instead of `core` + --> tests/ui/std_instead_of_core.rs:117:33 + | +LL | fn issue13158_msrv_1_81(_: &dyn std::error::Error) {} + | ^^^ help: consider importing the item from `core`: `core` + +error: aborting due to 17 previous errors diff --git a/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs index 5e71159ac6c5d..66d834b5e4279 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs +++ b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs @@ -14,3 +14,14 @@ fn issue15143() { //~^ std_instead_of_core //~| std_instead_of_alloc } + +#[rustfmt::skip] +fn pr16964() { + use std::{ + borrow::Cow, + //~^ std_instead_of_alloc + collections::BTreeSet, + //~^ std_instead_of_alloc + ffi::OsString, + }; +} diff --git a/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr index 147b46022126b..6fa8f47a4d6f4 100644 --- a/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr +++ b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr @@ -26,5 +26,21 @@ LL | use std::{error::Error, vec::Vec, fs::File}; = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]` -error: aborting due to 3 previous errors +error: used import from `std` instead of `alloc` + --> tests/ui/std_instead_of_core_unfixable.rs:21:17 + | +LL | borrow::Cow, + | ^^^ + | + = help: consider importing the item from `alloc` + +error: used import from `std` instead of `alloc` + --> tests/ui/std_instead_of_core_unfixable.rs:23:22 + | +LL | collections::BTreeSet, + | ^^^^^^^^ + | + = help: consider importing the item from `alloc` + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/uninit_vec.rs b/src/tools/clippy/tests/ui/uninit_vec.rs index a0bac28e87292..ae5c685d89abd 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.rs +++ b/src/tools/clippy/tests/ui/uninit_vec.rs @@ -205,3 +205,125 @@ fn main() { } } } + +mod issue_11715 { + use std::mem::MaybeUninit; + #[cfg(target_pointer_width = "64")] + const HUGE: usize = 4_294_967_296; + #[cfg(target_pointer_width = "32")] + const HUGE: usize = 268_435_455; + + fn large_maybeuninit_vec_ice() { + let mut v: Vec<[MaybeUninit; HUGE]> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + fn large_u8_vec_ice() { + let mut v: Vec<[u8; HUGE]> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + fn large_nested_maybeuninit_vec_ice() { + let mut v: Vec<[[MaybeUninit; HUGE]; 2]> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + struct HeavyWrapperSafe([MaybeUninit; HUGE]); + fn large_struct_maybeuninit_vec_ice() { + let mut v: Vec = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + struct HeavyWrapperUnsafe([u8; HUGE]); + fn large_struct_u8_vec_ice() { + let mut v: Vec = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + #[allow(clippy::large_enum_variant)] + enum HeavyEnum { + A([MaybeUninit; HUGE]), + B, + } + fn large_enum_vec_ice() { + let mut v: Vec = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + enum Uninhabited {} + fn uninhabited_enum() { + let mut v: Vec = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + enum SingleVariant { + OnlyOne, + } + fn single_variant_enum() { + let mut v: Vec = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + enum OneVariantU8 { + ThisOne([u8; HUGE]), + } + fn one_variant_u8() { + let mut v: Vec = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + enum OneVariantMaybeUninit { + ThisOne([MaybeUninit; HUGE]), + } + fn one_variant_maybe_uninit() { + let mut v: Vec = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + fn generic_vec_lints() { + let mut v: Vec = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + fn generic_vec_maybeuninit() { + let mut v: Vec> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + trait Assoc { + type Item; + } + fn projection_vec_lints() { + let mut v: Vec<::Item> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } + + struct Concrete; + impl Assoc for Concrete { + type Item = MaybeUninit; + } + fn normalized_projection_vec_ok() { + let mut v: Vec<::Item> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + + enum E { + Foo(MaybeUninit), + Bar(U), + } + fn enum_uninhabited_zst() { + let mut v: Vec> = Vec::with_capacity(1); + unsafe { v.set_len(1) }; + } + fn enum_uninhabited_non_zst() { + let mut v: Vec> = Vec::with_capacity(1); + //~^ uninit_vec + unsafe { v.set_len(1) }; + } +} diff --git a/src/tools/clippy/tests/ui/uninit_vec.stderr b/src/tools/clippy/tests/ui/uninit_vec.stderr index 1b821ef004e6f..56a182614b64b 100644 --- a/src/tools/clippy/tests/ui/uninit_vec.stderr +++ b/src/tools/clippy/tests/ui/uninit_vec.stderr @@ -159,5 +159,82 @@ LL | vec.set_len(1); | = help: initialize the buffer or wrap the content in `MaybeUninit` -error: aborting due to 15 previous errors +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:222:9 + | +LL | let mut v: Vec<[u8; HUGE]> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:240:9 + | +LL | let mut v: Vec = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:251:9 + | +LL | let mut v: Vec = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:274:9 + | +LL | let mut v: Vec = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:288:9 + | +LL | let mut v: Vec = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:302:9 + | +LL | let mut v: Vec<::Item> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: calling `set_len()` immediately after reserving a buffer creates uninitialized values + --> tests/ui/uninit_vec.rs:325:9 + | +LL | let mut v: Vec> = Vec::with_capacity(1); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | +LL | unsafe { v.set_len(1) }; + | ^^^^^^^^^^^^ + | + = help: initialize the buffer or wrap the content in `MaybeUninit` + +error: aborting due to 22 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_box_returns.rs b/src/tools/clippy/tests/ui/unnecessary_box_returns.rs index a7ab7e90be06b..e550996256f44 100644 --- a/src/tools/clippy/tests/ui/unnecessary_box_returns.rs +++ b/src/tools/clippy/tests/ui/unnecessary_box_returns.rs @@ -71,6 +71,23 @@ impl HasHuge { } } +// don't lint (issue #17202): the size of `[T; N]` depends on a const generic parameter +fn const_generic_array(value: Box<[T; N]>) -> Box<[T; N]> { + value +} + +// don't lint (issue #17202): the size of `[T; 10]` depends on the generic element type +fn generic_element_array(value: Box<[T; 10]>) -> Box<[T; 10]> { + value +} + +// lint: the size of `Vec` is known regardless of the generic element type +#[expect(clippy::box_collection)] +fn generic_vec(value: Box>) -> Box> { + //~^ unnecessary_box_returns + value +} + fn main() { // don't lint: this is a closure let a = || -> Box { Box::new(5) }; diff --git a/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr b/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr index ab1d90f13b922..8eb39fca5680b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_box_returns.stderr @@ -32,5 +32,13 @@ LL | fn _bxed_foo() -> Box { | = help: changing this also requires a change to the return expressions in this function -error: aborting due to 4 previous errors +error: boxed return of the sized type `std::vec::Vec` + --> tests/ui/unnecessary_box_returns.rs:86:42 + | +LL | fn generic_vec(value: Box>) -> Box> { + | ^^^^^^^^^^^ help: try: `std::vec::Vec` + | + = help: changing this also requires a change to the return expressions in this function + +error: aborting due to 5 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.fixed b/src/tools/clippy/tests/ui/unnecessary_cast.fixed index 1ecc3ecf57a00..9e07c52e82e67 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_cast.fixed @@ -1,7 +1,9 @@ //@aux-build:extern_fake_libc.rs +#![feature(const_trait_impl, const_ops)] #![warn(clippy::unnecessary_cast)] #![allow( clippy::borrow_as_ptr, + clippy::identity_op, clippy::multiple_bound_locations, clippy::no_effect, clippy::nonstandard_macro_braces, @@ -656,3 +658,25 @@ fn issue16475() -> *const u8 { //~^ unnecessary_cast } } + +// Make sure that the calculated values aren't changed by the fixes. +const _: () = { + use std::convert::identity; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0 == (!identity(!0_u64) as u64).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64 + 0).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!(0_u64 + 0)).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!((0 + 0) as u64)).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0_u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0_u64) as u64).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0_u64)).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0_u64 + 0).overflowing_shr(1_u32).0); + //~^ unnecessary_cast +}; diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.rs b/src/tools/clippy/tests/ui/unnecessary_cast.rs index 1a6cc831aa4b8..43d3e56e365a2 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.rs +++ b/src/tools/clippy/tests/ui/unnecessary_cast.rs @@ -1,7 +1,9 @@ //@aux-build:extern_fake_libc.rs +#![feature(const_trait_impl, const_ops)] #![warn(clippy::unnecessary_cast)] #![allow( clippy::borrow_as_ptr, + clippy::identity_op, clippy::multiple_bound_locations, clippy::no_effect, clippy::nonstandard_macro_braces, @@ -656,3 +658,25 @@ fn issue16475() -> *const u8 { //~^ unnecessary_cast } } + +// Make sure that the calculated values aren't changed by the fixes. +const _: () = { + use std::convert::identity; + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0 as u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64 as u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0 == (!identity(!0_u64) as u64).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0 as u64 + 0).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!(0 as u64 + 0)).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!((0 + 0) as u64)).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0 as u64).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0_u64) as u64).overflowing_shr(1_u32).0); + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0 as u64)).overflowing_shr(1_u32).0); + //~^ unnecessary_cast + assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0 as u64 + 0).overflowing_shr(1_u32).0); + //~^ unnecessary_cast +}; diff --git a/src/tools/clippy/tests/ui/unnecessary_cast.stderr b/src/tools/clippy/tests/ui/unnecessary_cast.stderr index 19d2afcb4f255..9807ae0341421 100644 --- a/src/tools/clippy/tests/ui/unnecessary_cast.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_cast.stderr @@ -1,5 +1,5 @@ error: casting raw pointers to the same type and constness is unnecessary (`*const T` -> `*const T`) - --> tests/ui/unnecessary_cast.rs:19:5 + --> tests/ui/unnecessary_cast.rs:21:5 | LL | ptr as *const T | ^^^^^^^^^^^^^^^ help: try: `ptr` @@ -8,388 +8,430 @@ LL | ptr as *const T = help: to override `-D warnings` add `#[allow(clippy::unnecessary_cast)]` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:55:5 + --> tests/ui/unnecessary_cast.rs:57:5 | LL | 1i32 as i32; | ^^^^^^^^^^^ help: try: `1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:57:5 + --> tests/ui/unnecessary_cast.rs:59:5 | LL | 1f32 as f32; | ^^^^^^^^^^^ help: try: `1_f32` error: casting to the same type is unnecessary (`bool` -> `bool`) - --> tests/ui/unnecessary_cast.rs:59:5 + --> tests/ui/unnecessary_cast.rs:61:5 | LL | false as bool; | ^^^^^^^^^^^^^ help: try: `false` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:63:5 + --> tests/ui/unnecessary_cast.rs:65:5 | LL | -1_i32 as i32; | ^^^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:65:5 + --> tests/ui/unnecessary_cast.rs:67:5 | LL | - 1_i32 as i32; | ^^^^^^^^^^^^^^ help: try: `- 1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:67:5 + --> tests/ui/unnecessary_cast.rs:69:5 | LL | -1f32 as f32; | ^^^^^^^^^^^^ help: try: `-1_f32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:69:5 + --> tests/ui/unnecessary_cast.rs:71:5 | LL | 1_i32 as i32; | ^^^^^^^^^^^^ help: try: `1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:71:5 + --> tests/ui/unnecessary_cast.rs:73:5 | LL | 1_f32 as f32; | ^^^^^^^^^^^^ help: try: `1_f32` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> tests/ui/unnecessary_cast.rs:74:22 + --> tests/ui/unnecessary_cast.rs:76:22 | LL | let _: *mut u8 = [1u8, 2].as_ptr() as *const u8 as *mut u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> tests/ui/unnecessary_cast.rs:77:5 + --> tests/ui/unnecessary_cast.rs:79:5 | LL | [1u8, 2].as_ptr() as *const u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*mut u8` -> `*mut u8`) - --> tests/ui/unnecessary_cast.rs:80:5 + --> tests/ui/unnecessary_cast.rs:82:5 | LL | [1u8, 2].as_mut_ptr() as *mut u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[1u8, 2].as_mut_ptr()` error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`) - --> tests/ui/unnecessary_cast.rs:92:5 + --> tests/ui/unnecessary_cast.rs:94:5 | LL | owo::([1u32].as_ptr()) as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `owo::([1u32].as_ptr())` error: casting raw pointers to the same type and constness is unnecessary (`*const u8` -> `*const u8`) - --> tests/ui/unnecessary_cast.rs:94:5 + --> tests/ui/unnecessary_cast.rs:96:5 | LL | uwu::([1u32].as_ptr()) as *const u8; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `uwu::([1u32].as_ptr())` error: casting raw pointers to the same type and constness is unnecessary (`*const u32` -> `*const u32`) - --> tests/ui/unnecessary_cast.rs:97:5 + --> tests/ui/unnecessary_cast.rs:99:5 | LL | uwu::([1u32].as_ptr()) as *const u32; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `uwu::([1u32].as_ptr())` error: casting to the same type is unnecessary (`u32` -> `u32`) - --> tests/ui/unnecessary_cast.rs:133:5 + --> tests/ui/unnecessary_cast.rs:135:5 | LL | aaa() as u32; | ^^^^^^^^^^^^ help: try: `aaa()` error: casting to the same type is unnecessary (`u32` -> `u32`) - --> tests/ui/unnecessary_cast.rs:136:5 + --> tests/ui/unnecessary_cast.rs:138:5 | LL | x as u32; | ^^^^^^^^ help: try: `x` error: casting to the same type is unnecessary (`u32` -> `u32`) - --> tests/ui/unnecessary_cast.rs:138:5 + --> tests/ui/unnecessary_cast.rs:140:5 | LL | bbb() as u32; | ^^^^^^^^^^^^ help: try: `bbb()` error: casting to the same type is unnecessary (`u32` -> `u32`) - --> tests/ui/unnecessary_cast.rs:141:5 + --> tests/ui/unnecessary_cast.rs:143:5 | LL | x as u32; | ^^^^^^^^ help: try: `x` error: casting integer literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:174:9 + --> tests/ui/unnecessary_cast.rs:176:9 | LL | 100 as f32; | ^^^^^^^^^^ help: try: `100_f32` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:176:9 + --> tests/ui/unnecessary_cast.rs:178:9 | LL | 100 as f64; | ^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:178:9 + --> tests/ui/unnecessary_cast.rs:180:9 | LL | 100_i32 as f64; | ^^^^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:180:17 + --> tests/ui/unnecessary_cast.rs:182:17 | LL | let _ = -100 as f32; | ^^^^^^^^^^^ help: try: `-100_f32` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:182:17 + --> tests/ui/unnecessary_cast.rs:184:17 | LL | let _ = -100 as f64; | ^^^^^^^^^^^ help: try: `-100_f64` error: casting integer literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:184:17 + --> tests/ui/unnecessary_cast.rs:186:17 | LL | let _ = -100_i32 as f64; | ^^^^^^^^^^^^^^^ help: try: `-100_f64` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:186:9 + --> tests/ui/unnecessary_cast.rs:188:9 | LL | 100. as f32; | ^^^^^^^^^^^ help: try: `100_f32` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:188:9 + --> tests/ui/unnecessary_cast.rs:190:9 | LL | 100. as f64; | ^^^^^^^^^^^ help: try: `100_f64` error: casting integer literal to `u32` is unnecessary - --> tests/ui/unnecessary_cast.rs:201:9 + --> tests/ui/unnecessary_cast.rs:203:9 | LL | 1 as u32; | ^^^^^^^^ help: try: `1_u32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:203:9 + --> tests/ui/unnecessary_cast.rs:205:9 | LL | 0x10 as i32; | ^^^^^^^^^^^ help: try: `0x10_i32` error: casting integer literal to `usize` is unnecessary - --> tests/ui/unnecessary_cast.rs:205:9 + --> tests/ui/unnecessary_cast.rs:207:9 | LL | 0b10 as usize; | ^^^^^^^^^^^^^ help: try: `0b10_usize` error: casting integer literal to `u16` is unnecessary - --> tests/ui/unnecessary_cast.rs:207:9 + --> tests/ui/unnecessary_cast.rs:209:9 | LL | 0o73 as u16; | ^^^^^^^^^^^ help: try: `0o73_u16` error: casting integer literal to `u32` is unnecessary - --> tests/ui/unnecessary_cast.rs:209:9 + --> tests/ui/unnecessary_cast.rs:211:9 | LL | 1_000_000_000 as u32; | ^^^^^^^^^^^^^^^^^^^^ help: try: `1_000_000_000_u32` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:212:9 + --> tests/ui/unnecessary_cast.rs:214:9 | LL | 1.0 as f64; | ^^^^^^^^^^ help: try: `1.0_f64` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:214:9 + --> tests/ui/unnecessary_cast.rs:216:9 | LL | 0.5 as f32; | ^^^^^^^^^^ help: try: `0.5_f32` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:219:17 + --> tests/ui/unnecessary_cast.rs:221:17 | LL | let _ = -1 as i32; | ^^^^^^^^^ help: try: `-1_i32` error: casting float literal to `f32` is unnecessary - --> tests/ui/unnecessary_cast.rs:221:17 + --> tests/ui/unnecessary_cast.rs:223:17 | LL | let _ = -1.0 as f32; | ^^^^^^^^^^^ help: try: `-1.0_f32` error: casting to the same type is unnecessary (`i32` -> `i32`) - --> tests/ui/unnecessary_cast.rs:228:18 + --> tests/ui/unnecessary_cast.rs:230:18 | LL | let _ = &(x as i32); | ^^^^^^^^^^ help: try: `{ x }` error: casting integer literal to `i32` is unnecessary - --> tests/ui/unnecessary_cast.rs:235:22 + --> tests/ui/unnecessary_cast.rs:237:22 | LL | let _: i32 = -(1) as i32; | ^^^^^^^^^^^ help: try: `-1_i32` error: casting integer literal to `i64` is unnecessary - --> tests/ui/unnecessary_cast.rs:238:22 + --> tests/ui/unnecessary_cast.rs:240:22 | LL | let _: i64 = -(1) as i64; | ^^^^^^^^^^^ help: try: `-1_i64` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:246:22 + --> tests/ui/unnecessary_cast.rs:248:22 | LL | let _: f64 = (-8.0 as f64).exp(); | ^^^^^^^^^^^^^ help: try: `(-8.0_f64)` error: casting float literal to `f64` is unnecessary - --> tests/ui/unnecessary_cast.rs:249:23 + --> tests/ui/unnecessary_cast.rs:251:23 | LL | let _: f64 = -(8.0 as f64).exp(); // should suggest `-8.0_f64.exp()` here not to change code behavior | ^^^^^^^^^^^^ help: try: `8.0_f64` error: casting to the same type is unnecessary (`f32` -> `f32`) - --> tests/ui/unnecessary_cast.rs:259:20 + --> tests/ui/unnecessary_cast.rs:261:20 | LL | let _num = foo() as f32; | ^^^^^^^^^^^^ help: try: `foo()` error: casting to the same type is unnecessary (`usize` -> `usize`) - --> tests/ui/unnecessary_cast.rs:270:9 + --> tests/ui/unnecessary_cast.rs:272:9 | LL | (*x as usize).pow(2) | ^^^^^^^^^^^^^ help: try: `(*x)` error: casting to the same type is unnecessary (`usize` -> `usize`) - --> tests/ui/unnecessary_cast.rs:278:31 + --> tests/ui/unnecessary_cast.rs:280:31 | LL | assert_eq!(vec.len(), x as usize); | ^^^^^^^^^^ help: try: `x` error: casting to the same type is unnecessary (`i64` -> `i64`) - --> tests/ui/unnecessary_cast.rs:281:17 + --> tests/ui/unnecessary_cast.rs:283:17 | LL | let _ = (5i32 as i64 as i64).abs(); | ^^^^^^^^^^^^^^^^^^^^ help: try: `(5i32 as i64)` error: casting to the same type is unnecessary (`i64` -> `i64`) - --> tests/ui/unnecessary_cast.rs:284:17 + --> tests/ui/unnecessary_cast.rs:286:17 | LL | let _ = 5i32 as i64 as i64; | ^^^^^^^^^^^^^^^^^^ help: try: `5i32 as i64` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:563:24 + --> tests/ui/unnecessary_cast.rs:565:24 | LL | id::>(1.0_f64.pow_like(2) as f64 * &a).view(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:567:26 + --> tests/ui/unnecessary_cast.rs:569:26 | LL | s.id::>(1.0_f64.pow_like(2) as f64 * &a).view(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:571:26 + --> tests/ui/unnecessary_cast.rs:573:26 | LL | wrap::>(1.0_f64.pow_like(2) as f64 * &a).inner.view(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:575:28 + --> tests/ui/unnecessary_cast.rs:577:28 | LL | s.wrap::>(1.0_f64.pow_like(2) as f64 * &a).inner.view(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:591:31 + --> tests/ui/unnecessary_cast.rs:593:31 | LL | let _ = receiver.take(1.0_f64.pow_like_single_impl(2) as f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:601:20 + --> tests/ui/unnecessary_cast.rs:603:20 | LL | let _ = id(1.0_f64.powi(2) as f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:604:22 + --> tests/ui/unnecessary_cast.rs:606:22 | LL | let _ = wrap(1.0_f64.powi(2) as f64).inner.abs(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:607:22 + --> tests/ui/unnecessary_cast.rs:609:22 | LL | let _ = s.id(1.0_f64.powi(2) as f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:610:24 + --> tests/ui/unnecessary_cast.rs:612:24 | LL | let _ = s.wrap(1.0_f64.powi(2) as f64).inner.abs(); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:613:20 + --> tests/ui/unnecessary_cast.rs:615:20 | LL | let _ = id(1.0_f64.powi(2) as f64 * &a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:616:22 + --> tests/ui/unnecessary_cast.rs:618:22 | LL | let _ = s.id(1.0_f64.powi(2) as f64 * &a); | ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:629:17 + --> tests/ui/unnecessary_cast.rs:631:17 | LL | let _ = 1.0_f64.pow_like(0.5) as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(0.5)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:632:17 + --> tests/ui/unnecessary_cast.rs:634:17 | LL | let _ = 1.0_f64.pow_like(2) as f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:635:17 + --> tests/ui/unnecessary_cast.rs:637:17 | LL | let _ = (1.0_f64.powi(2) as f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:638:17 + --> tests/ui/unnecessary_cast.rs:640:17 | LL | let _ = ((Y + 2) as f64).abs(); | ^^^^^^^^^^^^^^^^ help: try: `((Y + 2))` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:641:18 + --> tests/ui/unnecessary_cast.rs:643:18 | LL | let _ = (1.0_f64.pow_like_single_impl(2) as f64 + 1.0_f64).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:644:18 + --> tests/ui/unnecessary_cast.rs:646:18 | LL | let _ = (1.0_f64.pow_like_single_impl(2) as f64 + ONE).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)` error: casting to the same type is unnecessary (`f64` -> `f64`) - --> tests/ui/unnecessary_cast.rs:647:18 + --> tests/ui/unnecessary_cast.rs:649:18 | LL | let _ = (1.0_f64.pow_like_single_impl(2) as f64 + one).abs(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)` error: casting raw pointers to the same type and constness is unnecessary (`*const *const u8` -> `*const *const u8`) - --> tests/ui/unnecessary_cast.rs:655:10 + --> tests/ui/unnecessary_cast.rs:657:10 | LL | *(&NONE as *const _ as *const _ as *const *const u8 as *const *const u8) | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&NONE as *const _ as *const _ as *const *const u8)` -error: aborting due to 65 previous errors +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:665:45 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0 as u64).overflowing_shr(1_u32).0); + | ^^^^^^^^^^^ help: try: `(!0_u64)` + +error: casting to the same type is unnecessary (`u64` -> `u64`) + --> tests/ui/unnecessary_cast.rs:667:45 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0_u64 as u64).overflowing_shr(1_u32).0); + | ^^^^^^^^^^^^^^^ help: try: `(!0_u64)` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:670:46 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!0 as u64 + 0).overflowing_shr(1_u32).0); + | ^^^^^^^^^ help: try: `!0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:672:48 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!(0 as u64 + 0)).overflowing_shr(1_u32).0); + | ^^^^^^^^ help: try: `0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:675:54 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0 as u64).overflowing_shr(1_u32).0); + | ^^^^^^^^^ help: try: `!0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:678:56 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == (!identity(0 as u64)).overflowing_shr(1_u32).0); + | ^^^^^^^^ help: try: `0_u64` + +error: casting integer literal to `u64` is unnecessary + --> tests/ui/unnecessary_cast.rs:680:54 + | +LL | assert!(0x7f_ff_ff_ff_ff_ff_ff_ffu64 == identity(!0 as u64 + 0).overflowing_shr(1_u32).0); + | ^^^^^^^^^ help: try: `!0_u64` + +error: aborting due to 72 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed index 409a8efbfeb95..c8d6b1832a710 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed @@ -240,6 +240,18 @@ fn main() { let _: Result = res.or_else(|err| Ok(err)); } +fn issue11672() { + // needs turbofish to disambiguate + let _ = true.then_some::<&[u8]>({ &[] }); + //~^ unnecessary_lazy_evaluations +} + +fn issue11672_as() { + // Return type annotation helps type inference and removing it can break code + let _ = None.get_or_insert({ &[] } as &[u8]); + //~^ unnecessary_lazy_evaluations +} + #[allow(unused)] fn issue9485() { // should not lint, is in proc macro diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs index 54735023a935d..87e903c5bbb8c 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs @@ -240,6 +240,18 @@ fn main() { let _: Result = res.or_else(|err| Ok(err)); } +fn issue11672() { + // needs turbofish to disambiguate + let _ = true.then(|| -> &[u8] { &[] }); + //~^ unnecessary_lazy_evaluations +} + +fn issue11672_as() { + // Return type annotation helps type inference and removing it can break code + let _ = None.get_or_insert_with(|| -> &[u8] { &[] }); + //~^ unnecessary_lazy_evaluations +} + #[allow(unused)] fn issue9485() { // should not lint, is in proc macro diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr index 75a06a419c71b..9b91b22f80d3a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr @@ -484,7 +484,31 @@ LL + or(Ok(ext_str.some_field)); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:259:14 + --> tests/ui/unnecessary_lazy_eval.rs:245:13 + | +LL | let _ = true.then(|| -> &[u8] { &[] }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `then_some` instead + | +LL - let _ = true.then(|| -> &[u8] { &[] }); +LL + let _ = true.then_some::<&[u8]>({ &[] }); + | + +error: unnecessary closure used to substitute value for `Option::None` + --> tests/ui/unnecessary_lazy_eval.rs:251:13 + | +LL | let _ = None.get_or_insert_with(|| -> &[u8] { &[] }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `get_or_insert` instead + | +LL - let _ = None.get_or_insert_with(|| -> &[u8] { &[] }); +LL + let _ = None.get_or_insert({ &[] } as &[u8]); + | + +error: unnecessary closure used with `bool::then` + --> tests/ui/unnecessary_lazy_eval.rs:271:14 | LL | let _x = false.then(|| i32::MAX + 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -496,7 +520,7 @@ LL + let _x = false.then_some(i32::MAX + 1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:261:14 + --> tests/ui/unnecessary_lazy_eval.rs:273:14 | LL | let _x = false.then(|| i32::MAX * 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -508,7 +532,7 @@ LL + let _x = false.then_some(i32::MAX * 2); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:263:14 + --> tests/ui/unnecessary_lazy_eval.rs:275:14 | LL | let _x = false.then(|| i32::MAX - 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -520,7 +544,7 @@ LL + let _x = false.then_some(i32::MAX - 1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:265:14 + --> tests/ui/unnecessary_lazy_eval.rs:277:14 | LL | let _x = false.then(|| i32::MIN - 1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -532,7 +556,7 @@ LL + let _x = false.then_some(i32::MIN - 1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:267:14 + --> tests/ui/unnecessary_lazy_eval.rs:279:14 | LL | let _x = false.then(|| (1 + 2 * 3 - 2 / 3 + 9) << 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -544,7 +568,7 @@ LL + let _x = false.then_some((1 + 2 * 3 - 2 / 3 + 9) << 2); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:269:14 + --> tests/ui/unnecessary_lazy_eval.rs:281:14 | LL | let _x = false.then(|| 255u8 << 7); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -556,7 +580,7 @@ LL + let _x = false.then_some(255u8 << 7); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:271:14 + --> tests/ui/unnecessary_lazy_eval.rs:283:14 | LL | let _x = false.then(|| 255u8 << 8); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -568,7 +592,7 @@ LL + let _x = false.then_some(255u8 << 8); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:273:14 + --> tests/ui/unnecessary_lazy_eval.rs:285:14 | LL | let _x = false.then(|| 255u8 >> 8); | ^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -580,7 +604,7 @@ LL + let _x = false.then_some(255u8 >> 8); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:276:14 + --> tests/ui/unnecessary_lazy_eval.rs:288:14 | LL | let _x = false.then(|| i32::MAX + -1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -592,7 +616,7 @@ LL + let _x = false.then_some(i32::MAX + -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:278:14 + --> tests/ui/unnecessary_lazy_eval.rs:290:14 | LL | let _x = false.then(|| -i32::MAX); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -604,7 +628,7 @@ LL + let _x = false.then_some(-i32::MAX); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:280:14 + --> tests/ui/unnecessary_lazy_eval.rs:292:14 | LL | let _x = false.then(|| -i32::MIN); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -616,7 +640,7 @@ LL + let _x = false.then_some(-i32::MIN); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:283:14 + --> tests/ui/unnecessary_lazy_eval.rs:295:14 | LL | let _x = false.then(|| 255 >> -7); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -628,7 +652,7 @@ LL + let _x = false.then_some(255 >> -7); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:285:14 + --> tests/ui/unnecessary_lazy_eval.rs:297:14 | LL | let _x = false.then(|| 255 << -1); | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -640,7 +664,7 @@ LL + let _x = false.then_some(255 << -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:287:14 + --> tests/ui/unnecessary_lazy_eval.rs:299:14 | LL | let _x = false.then(|| 1 / 0); | ^^^^^^^^^^^^^^^^^^^^ @@ -652,7 +676,7 @@ LL + let _x = false.then_some(1 / 0); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:289:14 + --> tests/ui/unnecessary_lazy_eval.rs:301:14 | LL | let _x = false.then(|| x << -1); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -664,7 +688,7 @@ LL + let _x = false.then_some(x << -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:291:14 + --> tests/ui/unnecessary_lazy_eval.rs:303:14 | LL | let _x = false.then(|| x << 2); | ^^^^^^^^^^^^^^^^^^^^^ @@ -676,7 +700,7 @@ LL + let _x = false.then_some(x << 2); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:301:14 + --> tests/ui/unnecessary_lazy_eval.rs:313:14 | LL | let _x = false.then(|| x / 0); | ^^^^^^^^^^^^^^^^^^^^ @@ -688,7 +712,7 @@ LL + let _x = false.then_some(x / 0); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:303:14 + --> tests/ui/unnecessary_lazy_eval.rs:315:14 | LL | let _x = false.then(|| x % 0); | ^^^^^^^^^^^^^^^^^^^^ @@ -700,7 +724,7 @@ LL + let _x = false.then_some(x % 0); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:306:14 + --> tests/ui/unnecessary_lazy_eval.rs:318:14 | LL | let _x = false.then(|| 1 / -1); | ^^^^^^^^^^^^^^^^^^^^^ @@ -712,7 +736,7 @@ LL + let _x = false.then_some(1 / -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:308:14 + --> tests/ui/unnecessary_lazy_eval.rs:320:14 | LL | let _x = false.then(|| i32::MIN / -1); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -724,7 +748,7 @@ LL + let _x = false.then_some(i32::MIN / -1); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:311:14 + --> tests/ui/unnecessary_lazy_eval.rs:323:14 | LL | let _x = false.then(|| i32::MIN / 0); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -736,7 +760,7 @@ LL + let _x = false.then_some(i32::MIN / 0); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:313:14 + --> tests/ui/unnecessary_lazy_eval.rs:325:14 | LL | let _x = false.then(|| 4 / 2); | ^^^^^^^^^^^^^^^^^^^^ @@ -748,7 +772,7 @@ LL + let _x = false.then_some(4 / 2); | error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval.rs:321:14 + --> tests/ui/unnecessary_lazy_eval.rs:333:14 | LL | let _x = false.then(|| f1 + f2); | ^^^^^^^^^^^^^^^^^^^^^^ @@ -759,5 +783,5 @@ LL - let _x = false.then(|| f1 + f2); LL + let _x = false.then_some(f1 + f2); | -error: aborting due to 63 previous errors +error: aborting due to 65 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs index 6d28d544dfe0b..7ceda40597942 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.rs @@ -12,6 +12,8 @@ fn main() { // fix will break type inference let _ = Ok(1).unwrap_or_else(|()| 2); //~^ unnecessary_lazy_evaluations + let _ = Ok(1).unwrap_or_else(|()| -> i32 { 2 }); + //~^ unnecessary_lazy_evaluations mod e { pub struct E; @@ -26,9 +28,3 @@ fn main() { let arr = [(Some(1),)]; Some(&0).and_then(|&i| arr[i].0); } - -fn issue11672() { - // Return type annotation helps type inference and removing it can break code - let _ = true.then(|| -> &[u8] { &[] }); - //~^ unnecessary_lazy_evaluations -} diff --git a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr index 5174acc1e9f95..97e9ef621cee0 100644 --- a/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_lazy_eval_unfixable.stderr @@ -13,7 +13,19 @@ LL + let _ = Ok(1).unwrap_or(2); | error: unnecessary closure used to substitute value for `Result::Err` - --> tests/ui/unnecessary_lazy_eval_unfixable.rs:19:13 + --> tests/ui/unnecessary_lazy_eval_unfixable.rs:15:13 + | +LL | let _ = Ok(1).unwrap_or_else(|()| -> i32 { 2 }); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unwrap_or` instead + | +LL - let _ = Ok(1).unwrap_or_else(|()| -> i32 { 2 }); +LL + let _ = Ok(1).unwrap_or({ 2 } as i32); + | + +error: unnecessary closure used to substitute value for `Result::Err` + --> tests/ui/unnecessary_lazy_eval_unfixable.rs:21:13 | LL | let _ = Ok(1).unwrap_or_else(|e::E| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -25,7 +37,7 @@ LL + let _ = Ok(1).unwrap_or(2); | error: unnecessary closure used to substitute value for `Result::Err` - --> tests/ui/unnecessary_lazy_eval_unfixable.rs:22:13 + --> tests/ui/unnecessary_lazy_eval_unfixable.rs:24:13 | LL | let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,17 +48,5 @@ LL - let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2); LL + let _ = Ok(1).unwrap_or(2); | -error: unnecessary closure used with `bool::then` - --> tests/ui/unnecessary_lazy_eval_unfixable.rs:32:13 - | -LL | let _ = true.then(|| -> &[u8] { &[] }); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - | -help: use `then_some` instead - | -LL - let _ = true.then(|| -> &[u8] { &[] }); -LL + let _ = true.then_some({ &[] }); - | - error: aborting due to 4 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed index 317140eacc785..db31b339a08b0 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.fixed @@ -19,9 +19,9 @@ fn unnecessary_sort_by() { //~^ unnecessary_sort_by // Reverse examples vec.sort_by(|a, b| b.cmp(a)); // not linted to avoid suggesting `Reverse(b)` which would borrow - vec.sort_by_key(|b| std::cmp::Reverse((b + 5).abs())); + vec.sort_by_key(|a| std::cmp::Reverse((a + 5).abs())); //~^ unnecessary_sort_by - vec.sort_unstable_by_key(|b| std::cmp::Reverse(id(-b))); + vec.sort_unstable_by_key(|a| std::cmp::Reverse(id(-a))); //~^ unnecessary_sort_by // Negative examples (shouldn't be changed) let c = &7; @@ -99,9 +99,9 @@ mod issue_6001 { args.sort_unstable_by_key(|a| a.name()); //~^ unnecessary_sort_by // Reverse - args.sort_by_key(|b| std::cmp::Reverse(b.name())); + args.sort_by_key(|a| std::cmp::Reverse(a.name())); //~^ unnecessary_sort_by - args.sort_unstable_by_key(|b| std::cmp::Reverse(b.name())); + args.sort_unstable_by_key(|a| std::cmp::Reverse(a.name())); //~^ unnecessary_sort_by } } diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr b/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr index 56d4831eb70aa..868f7895fc069 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by.stderr @@ -57,7 +57,7 @@ LL | vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); help: try | LL - vec.sort_by(|a, b| (b + 5).abs().cmp(&(a + 5).abs())); -LL + vec.sort_by_key(|b| std::cmp::Reverse((b + 5).abs())); +LL + vec.sort_by_key(|a| std::cmp::Reverse((a + 5).abs())); | error: consider using `sort_unstable_by_key` @@ -69,7 +69,7 @@ LL | vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); help: try | LL - vec.sort_unstable_by(|a, b| id(-b).cmp(&id(-a))); -LL + vec.sort_unstable_by_key(|b| std::cmp::Reverse(id(-b))); +LL + vec.sort_unstable_by_key(|a| std::cmp::Reverse(id(-a))); | error: consider using `sort_by_key` @@ -129,7 +129,7 @@ LL | args.sort_by(|a, b| b.name().cmp(&a.name())); help: try | LL - args.sort_by(|a, b| b.name().cmp(&a.name())); -LL + args.sort_by_key(|b| std::cmp::Reverse(b.name())); +LL + args.sort_by_key(|a| std::cmp::Reverse(a.name())); | error: consider using `sort_unstable_by_key` @@ -141,7 +141,7 @@ LL | args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); help: try | LL - args.sort_unstable_by(|a, b| b.name().cmp(&a.name())); -LL + args.sort_unstable_by_key(|b| std::cmp::Reverse(b.name())); +LL + args.sort_unstable_by_key(|a| std::cmp::Reverse(a.name())); | error: consider using `sort_by_key` diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.fixed b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.fixed index 40e6aecd1b6d0..6fd740d87757a 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.fixed @@ -15,8 +15,8 @@ fn issue_11524() -> Vec { fn issue_11524_2() -> Vec { let mut vec = vec![1, 2, 3]; - // Should lint and suggest `vec.sort_by_key(|b| core::cmp::Reverse(b + 1));` - vec.sort_by_key(|b| core::cmp::Reverse(b + 1)); + // Should lint and suggest `vec.sort_by_key(|a| core::cmp::Reverse(a + 1));` + vec.sort_by_key(|a| core::cmp::Reverse(a + 1)); //~^ unnecessary_sort_by vec } diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.rs b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.rs index 184c90d959eb5..266553e7ace6b 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.rs +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.rs @@ -15,7 +15,7 @@ fn issue_11524() -> Vec { fn issue_11524_2() -> Vec { let mut vec = vec![1, 2, 3]; - // Should lint and suggest `vec.sort_by_key(|b| core::cmp::Reverse(b + 1));` + // Should lint and suggest `vec.sort_by_key(|a| core::cmp::Reverse(a + 1));` vec.sort_by(|a, b| (b + 1).cmp(&(a + 1))); //~^ unnecessary_sort_by vec diff --git a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.stderr b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.stderr index b4dd6a6dbdc52..26a8ad38fcd32 100644 --- a/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_sort_by_no_std.stderr @@ -21,7 +21,7 @@ LL | vec.sort_by(|a, b| (b + 1).cmp(&(a + 1))); help: try | LL - vec.sort_by(|a, b| (b + 1).cmp(&(a + 1))); -LL + vec.sort_by_key(|b| core::cmp::Reverse(b + 1)); +LL + vec.sort_by_key(|a| core::cmp::Reverse(a + 1)); | error: aborting due to 2 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.fixed b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.fixed index 499d9ee1ca235..f418fe954d400 100644 --- a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.fixed +++ b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.fixed @@ -26,9 +26,9 @@ fn simple() { println!{"Foo{{,}}"}; //~ unnecessary_trailing_comma println!{"Foo(,"}; //~ unnecessary_trailing_comma println!{"Foo[,"}; //~ unnecessary_trailing_comma + println!(concat!("Foo", "=", "{}"), 1); //~ unnecessary_trailing_comma // This should eventually work, but requires more work - println!(concat!("Foo", "=", "{}"), 1,); println!("No params", /*"a,){ */); println!("No params" /* "a,){*/, /*"a,){ */); diff --git a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.rs b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.rs index 15dea27b887b6..26770ffaeee18 100644 --- a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.rs +++ b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.rs @@ -26,9 +26,9 @@ fn simple() { println!{"Foo{{,}}", }; //~ unnecessary_trailing_comma println!{"Foo(,", }; //~ unnecessary_trailing_comma println!{"Foo[,", }; //~ unnecessary_trailing_comma + println!(concat!("Foo", "=", "{}"), 1,); //~ unnecessary_trailing_comma // This should eventually work, but requires more work - println!(concat!("Foo", "=", "{}"), 1,); println!("No params", /*"a,){ */); println!("No params" /* "a,){*/, /*"a,){ */); diff --git a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.stderr b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.stderr index 06fd5b1861a55..8ff7195a147b0 100644 --- a/src/tools/clippy/tests/ui/unnecessary_trailing_comma.stderr +++ b/src/tools/clippy/tests/ui/unnecessary_trailing_comma.stderr @@ -115,5 +115,11 @@ error: unnecessary trailing comma LL | println!{"Foo[,", }; | ^^ help: remove the trailing comma -error: aborting due to 19 previous errors +error: unnecessary trailing comma + --> tests/ui/unnecessary_trailing_comma.rs:29:42 + | +LL | println!(concat!("Foo", "=", "{}"), 1,); + | ^ help: remove the trailing comma + +error: aborting due to 20 previous errors diff --git a/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.fixed b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.fixed new file mode 100644 index 0000000000000..274bb7a00bfb0 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.fixed @@ -0,0 +1,104 @@ +//@aux-build:unnecessary_unwrap_unchecked_helper.rs +//@aux-build:proc_macros.rs +#![warn(clippy::unnecessary_unwrap_unchecked)] + +use proc_macros::{external, with_span}; + +#[rustfmt::skip] +use unnecessary_unwrap_unchecked_helper::{lol, kek}; + +mod b { + pub fn test_fn() -> Option { + Some(0) + } + + pub unsafe fn test_fn_unchecked() -> u32 { + 0 + } +} + +fn test_fn() -> Option { + Some(0) +} + +unsafe fn test_fn_unchecked() -> u32 { + 0 +} + +struct A; + +impl A { + fn a(&self) -> Option { + Some(0) + } + + unsafe fn a_unchecked(&self) -> u32 { + 0 + } + + fn an_assoc_fn() -> Option { + Some(0) + } + + unsafe fn an_assoc_fn_unchecked() -> u32 { + 0 + } +} + +struct B; + +impl B { + fn b(&self) -> Option { + Some(0) + } +} + +impl B { + unsafe fn b_unchecked(&self) -> u32 { + 0 + } +} + +fn main() { + let string_slice = unsafe { std::str::from_utf8_unchecked(&[]) }; + //~^ unnecessary_unwrap_unchecked + let a = unsafe { A::a_unchecked(&A) }; + //~^ unnecessary_unwrap_unchecked + let a = unsafe { + let a = A; + a.a_unchecked() + }; + //~^^ unnecessary_unwrap_unchecked + let an_assoc_fn = unsafe { A::an_assoc_fn_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let extern_fn = unsafe { unnecessary_unwrap_unchecked_helper::lol_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let local_fn = unsafe { b::test_fn_unchecked() }; + //~^ unnecessary_unwrap_unchecked + + // Don't lint: unlike `kek`, `kek_unchecked` isn't accessible from here + let extern_pub_fn = unsafe { kek().unwrap_unchecked() }; + + // Don't lint: `B::b` and `B::b_unchecked` come from distinct impl blocks + // (even though that wouldn't actually make the suggestion invalid in this case) + let b = unsafe { + let b = B; + b.b().unwrap_unchecked() + }; + + macro_rules! local { + () => {{ + unsafe { ::std::str::from_utf8(&[]).unwrap_unchecked() }; + }}; + } + local!(); + external! { + unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + } + with_span! { + span + unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.rs b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.rs new file mode 100644 index 0000000000000..006a541365a58 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.rs @@ -0,0 +1,104 @@ +//@aux-build:unnecessary_unwrap_unchecked_helper.rs +//@aux-build:proc_macros.rs +#![warn(clippy::unnecessary_unwrap_unchecked)] + +use proc_macros::{external, with_span}; + +#[rustfmt::skip] +use unnecessary_unwrap_unchecked_helper::{lol, kek}; + +mod b { + pub fn test_fn() -> Option { + Some(0) + } + + pub unsafe fn test_fn_unchecked() -> u32 { + 0 + } +} + +fn test_fn() -> Option { + Some(0) +} + +unsafe fn test_fn_unchecked() -> u32 { + 0 +} + +struct A; + +impl A { + fn a(&self) -> Option { + Some(0) + } + + unsafe fn a_unchecked(&self) -> u32 { + 0 + } + + fn an_assoc_fn() -> Option { + Some(0) + } + + unsafe fn an_assoc_fn_unchecked() -> u32 { + 0 + } +} + +struct B; + +impl B { + fn b(&self) -> Option { + Some(0) + } +} + +impl B { + unsafe fn b_unchecked(&self) -> u32 { + 0 + } +} + +fn main() { + let string_slice = unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let a = unsafe { A::a(&A).unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let a = unsafe { + let a = A; + a.a().unwrap_unchecked() + }; + //~^^ unnecessary_unwrap_unchecked + let an_assoc_fn = unsafe { A::an_assoc_fn().unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let extern_fn = unsafe { lol().unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol().unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + let local_fn = unsafe { b::test_fn().unwrap_unchecked() }; + //~^ unnecessary_unwrap_unchecked + + // Don't lint: unlike `kek`, `kek_unchecked` isn't accessible from here + let extern_pub_fn = unsafe { kek().unwrap_unchecked() }; + + // Don't lint: `B::b` and `B::b_unchecked` come from distinct impl blocks + // (even though that wouldn't actually make the suggestion invalid in this case) + let b = unsafe { + let b = B; + b.b().unwrap_unchecked() + }; + + macro_rules! local { + () => {{ + unsafe { ::std::str::from_utf8(&[]).unwrap_unchecked() }; + }}; + } + local!(); + external! { + unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + } + with_span! { + span + unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + } +} diff --git a/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.stderr b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.stderr new file mode 100644 index 0000000000000..359e50a33c9b7 --- /dev/null +++ b/src/tools/clippy/tests/ui/unnecessary_unwrap_unchecked.stderr @@ -0,0 +1,88 @@ +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:63:33 + | +LL | let string_slice = unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `-D clippy::unnecessary-unwrap-unchecked` implied by `-D warnings` + = help: to override `-D warnings` add `#[allow(clippy::unnecessary_unwrap_unchecked)]` +help: use `std::str::from_utf8_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let string_slice = unsafe { std::str::from_utf8(&[]).unwrap_unchecked() }; +LL + let string_slice = unsafe { std::str::from_utf8_unchecked(&[]) }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the method exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:65:22 + | +LL | let a = unsafe { A::a(&A).unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `A::a_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let a = unsafe { A::a(&A).unwrap_unchecked() }; +LL + let a = unsafe { A::a_unchecked(&A) }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the method exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:69:9 + | +LL | a.a().unwrap_unchecked() + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `A::a_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - a.a().unwrap_unchecked() +LL + a.a_unchecked() + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the associated function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:72:32 + | +LL | let an_assoc_fn = unsafe { A::an_assoc_fn().unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `A::an_assoc_fn_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let an_assoc_fn = unsafe { A::an_assoc_fn().unwrap_unchecked() }; +LL + let an_assoc_fn = unsafe { A::an_assoc_fn_unchecked() }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:74:30 + | +LL | let extern_fn = unsafe { lol().unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unnecessary_unwrap_unchecked_helper::lol_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let extern_fn = unsafe { lol().unwrap_unchecked() }; +LL + let extern_fn = unsafe { unnecessary_unwrap_unchecked_helper::lol_unchecked() }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:76:40 + | +LL | let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol().unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `unnecessary_unwrap_unchecked_helper::lol_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol().unwrap_unchecked() }; +LL + let extern_fn_qualified = unsafe { unnecessary_unwrap_unchecked_helper::lol_unchecked() }; + | + +error: usage of `unwrap_unchecked` when an `_unchecked` variant of the function exists + --> tests/ui/unnecessary_unwrap_unchecked.rs:78:29 + | +LL | let local_fn = unsafe { b::test_fn().unwrap_unchecked() }; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: use `b::test_fn_unchecked` instead, and remove the call to `.unwrap_unchecked()` + | +LL - let local_fn = unsafe { b::test_fn().unwrap_unchecked() }; +LL + let local_fn = unsafe { b::test_fn_unchecked() }; + | + +error: aborting due to 7 previous errors + diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml index 667468474b3c1..13ea2b250758c 100644 --- a/src/tools/clippy/triagebot.toml +++ b/src/tools/clippy/triagebot.toml @@ -60,6 +60,11 @@ labels = ["S-waiting-on-concerns"] [view-all-comments-link] threshold = 20 +# Allows `merge` and `delegate` commands for merging a PR +# Documentation at: https://forge.rust-lang.org/triagebot/merge.html +[merge] +type = "merge-queue" + [notify-zulip."lint-nominated"] zulip_stream = 577190 # #clippy/fcp topic = "FCP rust-clippy#{number}: {title}"