]> git.lizzy.rs Git - rust.git/commitdiff
Auto merge of #99422 - Dylan-DPC:rollup-htjofm6, r=Dylan-DPC
authorbors <bors@rust-lang.org>
Mon, 18 Jul 2022 20:39:24 +0000 (20:39 +0000)
committerbors <bors@rust-lang.org>
Mon, 18 Jul 2022 20:39:24 +0000 (20:39 +0000)
Rollup of 7 pull requests

Successful merges:

 - #98839 (Add assertion that `transmute_copy`'s U is not larger than T)
 - #98998 (Remove branch target prologues from `#[naked] fn`)
 - #99198 (add missing null ptr check in alloc example)
 - #99344 (rustdoc: avoid inlining items with duplicate `(type, name)`)
 - #99351 (Use `typeck_results` to get accurate qpath res for arg mismatch error)
 - #99378 (interpret/visitor: add missing early return)
 - #99394 (Add regression test for #95230)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup

167 files changed:
compiler/rustc_middle/src/ty/fold.rs
compiler/rustc_trait_selection/src/traits/project.rs
src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md
src/tools/clippy/.github/workflows/clippy_bors.yml
src/tools/clippy/CHANGELOG.md
src/tools/clippy/CONTRIBUTING.md
src/tools/clippy/book/README.md
src/tools/clippy/book/src/development/basics.md
src/tools/clippy/book/src/development/infrastructure/changelog_update.md
src/tools/clippy/book/src/usage.md
src/tools/clippy/clippy_dev/src/dogfood.rs [new file with mode: 0644]
src/tools/clippy/clippy_dev/src/lib.rs
src/tools/clippy/clippy_dev/src/main.rs
src/tools/clippy/clippy_dev/src/update_lints.rs
src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
src/tools/clippy/clippy_lints/src/casts/mod.rs
src/tools/clippy/clippy_lints/src/copies.rs
src/tools/clippy/clippy_lints/src/crate_in_macro_def.rs
src/tools/clippy/clippy_lints/src/dereference.rs
src/tools/clippy/clippy_lints/src/derive.rs
src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
src/tools/clippy/clippy_lints/src/duplicate_mod.rs
src/tools/clippy/clippy_lints/src/empty_drop.rs
src/tools/clippy/clippy_lints/src/entry.rs
src/tools/clippy/clippy_lints/src/equatable_if_let.rs
src/tools/clippy/clippy_lints/src/format_push_string.rs
src/tools/clippy/clippy_lints/src/inherent_impl.rs
src/tools/clippy/clippy_lints/src/invalid_utf8_in_unchecked.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/large_enum_variant.rs
src/tools/clippy/clippy_lints/src/lib.register_all.rs
src/tools/clippy/clippy_lints/src/lib.register_correctness.rs
src/tools/clippy/clippy_lints/src/lib.register_lints.rs
src/tools/clippy/clippy_lints/src/lib.register_perf.rs
src/tools/clippy/clippy_lints/src/lib.register_restriction.rs
src/tools/clippy/clippy_lints/src/lib.rs
src/tools/clippy/clippy_lints/src/loops/explicit_counter_loop.rs
src/tools/clippy/clippy_lints/src/loops/manual_find.rs
src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
src/tools/clippy/clippy_lints/src/manual_async_fn.rs
src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
src/tools/clippy/clippy_lints/src/matches/manual_map.rs
src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs
src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
src/tools/clippy/clippy_lints/src/matches/mod.rs
src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
src/tools/clippy/clippy_lints/src/methods/filter_map.rs
src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs
src/tools/clippy/clippy_lints/src/methods/mod.rs
src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
src/tools/clippy/clippy_lints/src/methods/unnecessary_lazy_eval.rs
src/tools/clippy/clippy_lints/src/misc.rs
src/tools/clippy/clippy_lints/src/mismatching_type_param_order.rs
src/tools/clippy/clippy_lints/src/new_without_default.rs
src/tools/clippy/clippy_lints/src/octal_escapes.rs
src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
src/tools/clippy/clippy_lints/src/option_if_let_else.rs
src/tools/clippy/clippy_lints/src/ptr.rs
src/tools/clippy/clippy_lints/src/question_mark.rs
src/tools/clippy/clippy_lints/src/regex.rs
src/tools/clippy/clippy_lints/src/shadow.rs
src/tools/clippy/clippy_lints/src/std_instead_of_core.rs [new file with mode: 0644]
src/tools/clippy/clippy_lints/src/swap_ptr_to_ref.rs
src/tools/clippy/clippy_lints/src/trait_bounds.rs
src/tools/clippy/clippy_lints/src/types/box_collection.rs
src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
src/tools/clippy/clippy_lints/src/unit_types/mod.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
src/tools/clippy/clippy_lints/src/write.rs
src/tools/clippy/clippy_utils/src/ast_utils.rs
src/tools/clippy/clippy_utils/src/hir_utils.rs
src/tools/clippy/clippy_utils/src/lib.rs
src/tools/clippy/clippy_utils/src/msrvs.rs
src/tools/clippy/clippy_utils/src/paths.rs
src/tools/clippy/clippy_utils/src/source.rs
src/tools/clippy/clippy_utils/src/sugg.rs
src/tools/clippy/clippy_utils/src/ty.rs
src/tools/clippy/clippy_utils/src/visitors.rs
src/tools/clippy/lintcheck/Cargo.toml
src/tools/clippy/lintcheck/README.md
src/tools/clippy/lintcheck/src/main.rs
src/tools/clippy/rust-toolchain
src/tools/clippy/tests/compile-test.rs
src/tools/clippy/tests/dogfood.rs
src/tools/clippy/tests/lint_message_convention.rs
src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/d.rs
src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.rs
src/tools/clippy/tests/ui-cargo/duplicate_mod/fail/src/main.stderr
src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
src/tools/clippy/tests/ui/box_collection.rs
src/tools/clippy/tests/ui/box_collection.stderr
src/tools/clippy/tests/ui/branches_sharing_code/false_positives.rs
src/tools/clippy/tests/ui/cast_size_32bit.stderr
src/tools/clippy/tests/ui/crashes/ice-9041.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/crashes/ice-9041.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/equatable_if_let.fixed
src/tools/clippy/tests/ui/equatable_if_let.rs
src/tools/clippy/tests/ui/equatable_if_let.stderr
src/tools/clippy/tests/ui/explicit_auto_deref.fixed
src/tools/clippy/tests/ui/explicit_auto_deref.rs
src/tools/clippy/tests/ui/explicit_auto_deref.stderr
src/tools/clippy/tests/ui/if_let_mutex.rs
src/tools/clippy/tests/ui/inconsistent_struct_constructor.fixed
src/tools/clippy/tests/ui/inconsistent_struct_constructor.rs
src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/let_unit.fixed
src/tools/clippy/tests/ui/let_unit.rs
src/tools/clippy/tests/ui/let_unit.stderr
src/tools/clippy/tests/ui/manual_filter_map.fixed
src/tools/clippy/tests/ui/manual_filter_map.rs
src/tools/clippy/tests/ui/manual_filter_map.stderr
src/tools/clippy/tests/ui/manual_find_map.fixed
src/tools/clippy/tests/ui/manual_find_map.rs
src/tools/clippy/tests/ui/manual_find_map.stderr
src/tools/clippy/tests/ui/manual_flatten.rs
src/tools/clippy/tests/ui/manual_flatten.stderr
src/tools/clippy/tests/ui/map_flatten_fixable.fixed
src/tools/clippy/tests/ui/map_flatten_fixable.rs
src/tools/clippy/tests/ui/map_flatten_fixable.stderr
src/tools/clippy/tests/ui/match_expr_like_matches_macro.fixed
src/tools/clippy/tests/ui/match_expr_like_matches_macro.rs
src/tools/clippy/tests/ui/match_expr_like_matches_macro.stderr
src/tools/clippy/tests/ui/needless_borrow.fixed
src/tools/clippy/tests/ui/needless_borrow.rs
src/tools/clippy/tests/ui/needless_match.fixed
src/tools/clippy/tests/ui/needless_match.rs
src/tools/clippy/tests/ui/needless_match.stderr
src/tools/clippy/tests/ui/new_without_default.rs
src/tools/clippy/tests/ui/no_effect.rs
src/tools/clippy/tests/ui/no_effect.stderr
src/tools/clippy/tests/ui/or_fun_call.fixed
src/tools/clippy/tests/ui/or_fun_call.stderr
src/tools/clippy/tests/ui/question_mark.fixed
src/tools/clippy/tests/ui/question_mark.rs
src/tools/clippy/tests/ui/question_mark.stderr
src/tools/clippy/tests/ui/same_name_method.rs
src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed
src/tools/clippy/tests/ui/search_is_some_fixable_some.rs
src/tools/clippy/tests/ui/search_is_some_fixable_some.stderr
src/tools/clippy/tests/ui/shadow.stderr
src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs
src/tools/clippy/tests/ui/std_instead_of_core.rs [new file with mode: 0644]
src/tools/clippy/tests/ui/std_instead_of_core.stderr [new file with mode: 0644]
src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs
src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr
src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
src/tools/clippy/tests/ui/type_repetition_in_bounds.rs
src/tools/clippy/tests/ui/type_repetition_in_bounds.stderr
src/tools/clippy/tests/ui/undocumented_unsafe_blocks.rs
src/tools/clippy/tests/ui/undocumented_unsafe_blocks.stderr
src/tools/clippy/tests/ui/unnecessary_lazy_eval.fixed
src/tools/clippy/tests/ui/unnecessary_lazy_eval.rs
src/tools/clippy/tests/ui/unnecessary_lazy_eval.stderr
src/tools/clippy/tests/ui/while_let_on_iterator.fixed
src/tools/clippy/tests/ui/while_let_on_iterator.rs
src/tools/clippy/tests/ui/while_let_on_iterator.stderr
src/tools/clippy/tests/ui/wildcard_imports.fixed
src/tools/clippy/tests/ui/wildcard_imports.rs
src/tools/clippy/util/etc/vscode-tasks.json
src/tools/clippy/util/gh-pages/index.html
src/tools/clippy/util/gh-pages/script.js

index f8893ae29f58ee8eb0f7ae8f9e1d93fece10a516..878f31af00f07e867493bd2748a10243b9bb425a 100644 (file)
@@ -445,10 +445,13 @@ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
                 let ct = (self.fld_c)(bound_const, ct.ty());
                 ty::fold::shift_vars(self.tcx, ct, self.current_index.as_u32())
             }
-            _ if ct.has_vars_bound_at_or_above(self.current_index) => ct.super_fold_with(self),
-            _ => ct,
+            _ => ct.super_fold_with(self),
         }
     }
+
+    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
+    }
 }
 
 impl<'tcx> TyCtxt<'tcx> {
index b3e7fbb35782860a847a54968614d9a58084434d..9de4d3a646cb306e2264b6e4179a75336b6600df 100644 (file)
@@ -753,10 +753,13 @@ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
                     .tcx
                     .mk_const(ty::ConstS { kind: ty::ConstKind::Placeholder(p), ty: ct.ty() })
             }
-            _ if ct.has_vars_bound_at_or_above(self.current_index) => ct.super_fold_with(self),
-            _ => ct,
+            _ => ct.super_fold_with(self),
         }
     }
+
+    fn fold_predicate(&mut self, p: ty::Predicate<'tcx>) -> ty::Predicate<'tcx> {
+        if p.has_vars_bound_at_or_above(self.current_index) { p.super_fold_with(self) } else { p }
+    }
 }
 
 // The inverse of `BoundVarReplacer`: replaces placeholders with the bound vars from which they came.
index 780ee9d63dfd298afbe988e78d88d0a65b26d6d2..9e49f60892d26490c047173c568d15e9690f82e3 100644 (file)
@@ -3,11 +3,15 @@ Thank you for making Clippy better!
 We're collecting our changelog from pull request descriptions.
 If your PR only includes internal changes, you can just write
 `changelog: none`. Otherwise, please write a short comment
-explaining your change. Also, it's helpful for us that
-the lint name is put into brackets `[]` and backticks `` ` ` ``,
-e.g. ``[`lint_name`]``.
+explaining your change.
 
-If your PR fixes an issue, you can add "fixes #issue_number" into this
+It's also helpful for us that the lint name is put within backticks (`` ` ` ``),
+and then encapsulated by square brackets (`[]`), for example:
+```
+changelog: [`lint_name`]: your change
+```
+
+If your PR fixes an issue, you can add `fixes #issue_number` into this
 PR description. This way the issue will be automatically closed when
 your PR is merged.
 
index b8ea424ef3483152763d7b7e83e475a55635fdf8..97453303cd6aae58c0959ede46d21704e254464e 100644 (file)
@@ -144,7 +144,7 @@ jobs:
         OS: ${{ runner.os }}
 
   metadata_collection:
-    needs: base
+    needs: changelog
     runs-on: ubuntu-latest
 
     steps:
@@ -264,7 +264,7 @@ jobs:
     name: bors test finished
     if: github.event.pusher.name == 'bors' && success()
     runs-on: ubuntu-latest
-    needs: [changelog, base, integration_build, integration]
+    needs: [changelog, base, metadata_collection, integration_build, integration]
 
     steps:
       - name: Mark the job as successful
@@ -274,7 +274,7 @@ jobs:
     name: bors test finished
     if: github.event.pusher.name == 'bors' && (failure() || cancelled())
     runs-on: ubuntu-latest
-    needs: [changelog, base, integration_build, integration]
+    needs: [changelog, base, metadata_collection, integration_build, integration]
 
     steps:
       - name: Mark the job as a failure
index 9bc93c1cb42c91ad045e77c9a5c88f83abdf095b..920d397add716b742e032d8cdd103d59b94007a2 100644 (file)
 # Changelog
 
 All notable changes to this project will be documented in this file.
-See [Changelog Update](doc/changelog_update.md) if you want to update this
+See [Changelog Update](book/src/development/infrastructure/changelog_update.md) if you want to update this
 document.
 
 ## Unreleased / In Rust Nightly
 
-[d0cf3481...master](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...master)
+[7c21f91b...master](https://github.com/rust-lang/rust-clippy/compare/7c21f91b...master)
+
+## Rust 1.62
+
+Current stable, released 2022-06-30
+
+[d0cf3481...7c21f91b](https://github.com/rust-lang/rust-clippy/compare/d0cf3481...7c21f91b)
+
+### New Lints
+
+* [`large_include_file`]
+  [#8727](https://github.com/rust-lang/rust-clippy/pull/8727)
+* [`cast_abs_to_unsigned`]
+  [#8635](https://github.com/rust-lang/rust-clippy/pull/8635)
+* [`err_expect`]
+  [#8606](https://github.com/rust-lang/rust-clippy/pull/8606)
+* [`unnecessary_owned_empty_strings`]
+  [#8660](https://github.com/rust-lang/rust-clippy/pull/8660)
+* [`empty_structs_with_brackets`]
+  [#8594](https://github.com/rust-lang/rust-clippy/pull/8594)
+* [`crate_in_macro_def`]
+  [#8576](https://github.com/rust-lang/rust-clippy/pull/8576)
+* [`needless_option_take`]
+  [#8665](https://github.com/rust-lang/rust-clippy/pull/8665)
+* [`bytes_count_to_len`]
+  [#8711](https://github.com/rust-lang/rust-clippy/pull/8711)
+* [`is_digit_ascii_radix`]
+  [#8624](https://github.com/rust-lang/rust-clippy/pull/8624)
+* [`await_holding_invalid_type`]
+  [#8707](https://github.com/rust-lang/rust-clippy/pull/8707)
+* [`trim_split_whitespace`]
+  [#8575](https://github.com/rust-lang/rust-clippy/pull/8575)
+* [`pub_use`]
+  [#8670](https://github.com/rust-lang/rust-clippy/pull/8670)
+* [`format_push_string`]
+  [#8626](https://github.com/rust-lang/rust-clippy/pull/8626)
+* [`empty_drop`]
+  [#8571](https://github.com/rust-lang/rust-clippy/pull/8571)
+* [`drop_non_drop`]
+  [#8630](https://github.com/rust-lang/rust-clippy/pull/8630)
+* [`forget_non_drop`]
+  [#8630](https://github.com/rust-lang/rust-clippy/pull/8630)
+
+### Moves and Deprecations
+
+* Move [`only_used_in_recursion`] to `nursery` (now allow-by-default)
+  [#8783](https://github.com/rust-lang/rust-clippy/pull/8783)
+* Move [`stable_sort_primitive`] to `pedantic` (now allow-by-default)
+  [#8716](https://github.com/rust-lang/rust-clippy/pull/8716)
+
+### Enhancements
+
+* Remove overlap between [`manual_split_once`] and [`needless_splitn`]
+  [#8631](https://github.com/rust-lang/rust-clippy/pull/8631)
+* [`map_identity`]: Now checks for needless `map_err`
+  [#8487](https://github.com/rust-lang/rust-clippy/pull/8487)
+* [`extra_unused_lifetimes`]: Now checks for impl lifetimes
+  [#8737](https://github.com/rust-lang/rust-clippy/pull/8737)
+* [`cast_possible_truncation`]: Now catches more cases with larger shift or divide operations
+  [#8687](https://github.com/rust-lang/rust-clippy/pull/8687)
+* [`identity_op`]: Now checks for modulo expressions
+  [#8519](https://github.com/rust-lang/rust-clippy/pull/8519)
+* [`panic`]: No longer lint in constant context
+  [#8592](https://github.com/rust-lang/rust-clippy/pull/8592)
+* [`manual_split_once`]: Now lints manual iteration of `splitn`
+  [#8717](https://github.com/rust-lang/rust-clippy/pull/8717)
+* [`self_named_module_files`], [`mod_module_files`]: Now handle relative module paths
+  [#8611](https://github.com/rust-lang/rust-clippy/pull/8611)
+* [`unsound_collection_transmute`]: Now has better size and alignment checks
+  [#8648](https://github.com/rust-lang/rust-clippy/pull/8648)
+* [`unnested_or_patterns`]: Ignore cases, where the suggestion would be longer
+  [#8619](https://github.com/rust-lang/rust-clippy/pull/8619)
+
+### False Positive Fixes
+
+* [`rest_pat_in_fully_bound_structs`]: Now ignores structs marked with `#[non_exhaustive]`
+  [#8690](https://github.com/rust-lang/rust-clippy/pull/8690)
+* [`needless_late_init`]: No longer lints `if let` statements, `let mut` bindings or instances that
+  changes the drop order significantly
+  [#8617](https://github.com/rust-lang/rust-clippy/pull/8617)
+* [`unnecessary_cast`]: No longer lints to casts to aliased or non-primitive types
+  [#8596](https://github.com/rust-lang/rust-clippy/pull/8596)
+* [`init_numbered_fields`]: No longer lints type aliases
+  [#8780](https://github.com/rust-lang/rust-clippy/pull/8780)
+* [`needless_option_as_deref`]: No longer lints for `as_deref_mut` on `Option` values that can't be moved
+  [#8646](https://github.com/rust-lang/rust-clippy/pull/8646)
+* [`mistyped_literal_suffixes`]: Now ignores float literals without an exponent
+  [#8742](https://github.com/rust-lang/rust-clippy/pull/8742)
+* [`undocumented_unsafe_blocks`]: Now ignores unsafe blocks from proc-macros and works better for sub-expressions
+  [#8450](https://github.com/rust-lang/rust-clippy/pull/8450)
+* [`same_functions_in_if_condition`]: Now allows different constants, even if they have the same value
+  [#8673](https://github.com/rust-lang/rust-clippy/pull/8673)
+* [`needless_match`]: Now checks for more complex types and ignores type coercion
+  [#8549](https://github.com/rust-lang/rust-clippy/pull/8549)
+* [`assertions_on_constants`]: Now ignores constants from `cfg!` macros
+  [#8614](https://github.com/rust-lang/rust-clippy/pull/8614)
+* [`indexing_slicing`]: Fix false positives with constant indices in
+  [#8588](https://github.com/rust-lang/rust-clippy/pull/8588)
+* [`iter_with_drain`]: Now ignores iterator references
+  [#8668](https://github.com/rust-lang/rust-clippy/pull/8668)
+* [`useless_attribute`]: Now allows [`redundant_pub_crate`] on `use` items
+  [#8743](https://github.com/rust-lang/rust-clippy/pull/8743)
+* [`cast_ptr_alignment`]: Now ignores expressions, when used for unaligned reads and writes
+  [#8632](https://github.com/rust-lang/rust-clippy/pull/8632)
+* [`wrong_self_convention`]: Now allows `&mut self` and no self as arguments for `is_*` methods
+  [#8738](https://github.com/rust-lang/rust-clippy/pull/8738)
+* [`mut_from_ref`]: Only lint in unsafe code
+  [#8647](https://github.com/rust-lang/rust-clippy/pull/8647)
+* [`redundant_pub_crate`]: Now allows macro exports
+  [#8736](https://github.com/rust-lang/rust-clippy/pull/8736)
+* [`needless_match`]: Ignores cases where the else block expression is different
+  [#8700](https://github.com/rust-lang/rust-clippy/pull/8700)
+* [`transmute_int_to_char`]: Now allows transmutations in `const` code
+  [#8610](https://github.com/rust-lang/rust-clippy/pull/8610)
+* [`manual_non_exhaustive`]: Ignores cases, where the enum value is used
+  [#8645](https://github.com/rust-lang/rust-clippy/pull/8645)
+* [`redundant_closure`]: Now ignores coerced closure
+  [#8431](https://github.com/rust-lang/rust-clippy/pull/8431)
+* [`identity_op`]: Is now ignored in cases where extra brackets would be needed
+  [#8730](https://github.com/rust-lang/rust-clippy/pull/8730)
+* [`let_unit_value`]: Now ignores cases which are used for type inference
+  [#8563](https://github.com/rust-lang/rust-clippy/pull/8563)
+
+### Suggestion Fixes/Improvements
+
+* [`manual_split_once`]: Fixed incorrect suggestions for single result accesses
+  [#8631](https://github.com/rust-lang/rust-clippy/pull/8631)
+* [`bytes_nth`]: Fix typos in the diagnostic message
+  [#8403](https://github.com/rust-lang/rust-clippy/pull/8403)
+* [`mistyped_literal_suffixes`]: Now suggests the correct integer types
+  [#8742](https://github.com/rust-lang/rust-clippy/pull/8742)
+* [`unnecessary_to_owned`]: Fixed suggestion based on the configured msrv
+  [#8692](https://github.com/rust-lang/rust-clippy/pull/8692)
+* [`single_element_loop`]: Improve lint for Edition 2021 arrays
+  [#8616](https://github.com/rust-lang/rust-clippy/pull/8616)
+* [`manual_bits`]: Now includes a cast for proper type conversion, when needed
+  [#8677](https://github.com/rust-lang/rust-clippy/pull/8677)
+* [`option_map_unit_fn`], [`result_map_unit_fn`]: Fix some incorrect suggestions
+  [#8584](https://github.com/rust-lang/rust-clippy/pull/8584)
+* [`collapsible_else_if`]: Add whitespace in suggestion
+  [#8729](https://github.com/rust-lang/rust-clippy/pull/8729)
+* [`transmute_bytes_to_str`]: Now suggest `from_utf8_unchecked` in `const` context
+  [#8612](https://github.com/rust-lang/rust-clippy/pull/8612)
+* [`map_clone`]: Improve message and suggestion based on the msrv
+  [#8688](https://github.com/rust-lang/rust-clippy/pull/8688)
+* [`needless_late_init`]: Now shows the `let` statement where it was first initialized
+  [#8779](https://github.com/rust-lang/rust-clippy/pull/8779)
+
+### ICE Fixes
+
+* [`only_used_in_recursion`]
+  [#8691](https://github.com/rust-lang/rust-clippy/pull/8691)
+* [`cast_slice_different_sizes`]
+  [#8720](https://github.com/rust-lang/rust-clippy/pull/8720)
+* [`iter_overeager_cloned`]
+  [#8602](https://github.com/rust-lang/rust-clippy/pull/8602)
+* [`undocumented_unsafe_blocks`]
+  [#8686](https://github.com/rust-lang/rust-clippy/pull/8686)
 
 ## Rust 1.61
 
-Current stable, released 2022-05-19
+Released 2022-05-19
 
 [57b3c4b...d0cf3481](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...d0cf3481)
 
@@ -207,7 +364,7 @@ Released 2022-04-07
 
 * [`needless_borrow`]: Prevent mutable borrows being moved and suggest removing the borrow on method calls
   [#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
-* [`chars_next_cmp`]: Correctly excapes the suggestion
+* [`chars_next_cmp`]: Correctly escapes the suggestion
   [#8376](https://github.com/rust-lang/rust-clippy/pull/8376)
 * [`explicit_write`]: Add suggestions for `write!`s with format arguments
   [#8365](https://github.com/rust-lang/rust-clippy/pull/8365)
@@ -1420,7 +1577,7 @@ Released 2021-03-25
 * Add `cargo dev-lintcheck` tool to the Clippy Dev Tool
   [#6469](https://github.com/rust-lang/rust-clippy/pull/6469)
 
-[Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/doc/roadmap-2021.md
+[Roadmap]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/proposals/roadmap-2021.md
 [Roadmap project page]: https://github.com/rust-lang/rust-clippy/projects/3
 
 ## Rust 1.50
@@ -2368,7 +2525,7 @@ Released 2019-09-26
   * [`inherent_to_string_shadow_display`] [#4259](https://github.com/rust-lang/rust-clippy/pull/4259)
   * [`type_repetition_in_bounds`] [#3766](https://github.com/rust-lang/rust-clippy/pull/3766)
   * [`try_err`] [#4222](https://github.com/rust-lang/rust-clippy/pull/4222)
-* Move `{unnnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
+* Move `{unnecessary,panicking}_unwrap` out of nursery [#4307](https://github.com/rust-lang/rust-clippy/pull/4307)
 * Extend the `use_self` lint to suggest uses of `Self::Variant` [#4308](https://github.com/rust-lang/rust-clippy/pull/4308)
 * Improve suggestion for needless return [#4262](https://github.com/rust-lang/rust-clippy/pull/4262)
 * Add auto-fixable suggestion for `let_unit` [#4337](https://github.com/rust-lang/rust-clippy/pull/4337)
@@ -3269,12 +3426,13 @@ Released 2018-09-13
 [`AsRef`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html
 [configuration file]: ./rust-clippy#configuration
 [pull3665]: https://github.com/rust-lang/rust-clippy/pull/3665
-[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
+[adding_lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/adding_lints.md
 [`README.md`]: https://github.com/rust-lang/rust-clippy/blob/master/README.md
 
 <!-- lint disable no-unused-definitions -->
 <!-- begin autogenerated links to lint list -->
 [`absurd_extreme_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#absurd_extreme_comparisons
+[`alloc_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#alloc_instead_of_core
 [`allow_attributes_without_reason`]: https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes_without_reason
 [`almost_complete_letter_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_letter_range
 [`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
@@ -3484,6 +3642,7 @@ Released 2018-09-13
 [`invalid_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_ref
 [`invalid_regex`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_regex
 [`invalid_upcast_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_upcast_comparisons
+[`invalid_utf8_in_unchecked`]: https://rust-lang.github.io/rust-clippy/master/index.html#invalid_utf8_in_unchecked
 [`invisible_characters`]: https://rust-lang.github.io/rust-clippy/master/index.html#invisible_characters
 [`is_digit_ascii_radix`]: https://rust-lang.github.io/rust-clippy/master/index.html#is_digit_ascii_radix
 [`items_after_statements`]: https://rust-lang.github.io/rust-clippy/master/index.html#items_after_statements
@@ -3743,6 +3902,8 @@ Released 2018-09-13
 [`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
 [`slow_vector_initialization`]: https://rust-lang.github.io/rust-clippy/master/index.html#slow_vector_initialization
 [`stable_sort_primitive`]: https://rust-lang.github.io/rust-clippy/master/index.html#stable_sort_primitive
+[`std_instead_of_alloc`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_alloc
+[`std_instead_of_core`]: https://rust-lang.github.io/rust-clippy/master/index.html#std_instead_of_core
 [`str_to_string`]: https://rust-lang.github.io/rust-clippy/master/index.html#str_to_string
 [`string_add`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add
 [`string_add_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#string_add_assign
index e81e7ceedcb50fe372a273cc5ee1c2b710b28dbe..6e15133d267ba04cfa304a2f7f73b909cfa4fee7 100644 (file)
@@ -16,7 +16,6 @@ All contributors are expected to follow the [Rust Code of Conduct].
   - [The Clippy book](#the-clippy-book)
   - [High level approach](#high-level-approach)
   - [Finding something to fix/improve](#finding-something-to-fiximprove)
-  - [Writing code](#writing-code)
   - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work)
     - [IntelliJ Rust](#intellij-rust)
     - [Rust Analyzer](#rust-analyzer)
@@ -115,11 +114,11 @@ As of [#6869][6869], [`rust-analyzer`][ra_homepage] can understand that Clippy u
 using `extern crate` when `package.metadata.rust-analyzer.rustc_private` is set to `true` in Clippy's `Cargo.toml.`
 You will require a `nightly` toolchain with the `rustc-dev` component installed.
 Make sure that in the `rust-analyzer` configuration, you set
-```
-{ "rust-analyzer.rustcSource": "discover" }
+```json
+{ "rust-analyzer.rustc.source": "discover" }
 ```
 and
-```
+```json
 { "rust-analyzer.updates.channel": "nightly" }
 ```
 You should be able to see information on things like `Expr` or `EarlyContext` now if you hover them, also
index b652194d0d13b25ec38c99d56460923b5ab7b02f..6d67f80ff256520d73cfe2f5198485d6e07a329c 100644 (file)
@@ -1,4 +1,4 @@
 # Clippy Book
 
 This is the source for the Clippy Book. See the
-[book](src/infrastructure/book.md) for more information.
+[book](src/development/infrastructure/book.md) for more information.
index 605897ff49cdb4dc42d468e2976cef59e7f8d9d2..44ba6e32755e319b24d5797c0b95c42baddaad20 100644 (file)
@@ -4,8 +4,8 @@ This document explains the basics for hacking on Clippy. Besides others, this
 includes how to build and test Clippy. For a more in depth description on the
 codebase take a look at [Adding Lints] or [Common Tools].
 
-[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md
-[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md
+[Adding Lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/adding_lints.md
+[Common Tools]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md
 
 - [Basics for hacking on Clippy](#basics-for-hacking-on-clippy)
   - [Get the Code](#get-the-code)
@@ -59,7 +59,7 @@ cargo uitest
 # only run UI tests starting with `test_`
 TESTNAME="test_" cargo uitest
 # only run dogfood tests
-cargo test --test dogfood
+cargo dev dogfood
 ```
 
 If the output of a [UI test] differs from the expected output, you can update
@@ -91,10 +91,14 @@ cargo dev fmt
 cargo dev update_lints
 # create a new lint and register it
 cargo dev new_lint
+# deprecate a lint and attempt to remove code relating to it
+cargo dev deprecate
 # automatically formatting all code before each commit
 cargo dev setup git-hook
 # (experimental) Setup Clippy to work with IntelliJ-Rust
 cargo dev setup intellij
+# runs the `dogfood` tests
+cargo dev dogfood
 ```
 
 More about intellij command usage and reasons
index e560f4c6a3e51d5b752af5b7c4479509469ac5f0..80a47affe30d0c5eae3cfd392e527f129b59b4bb 100644 (file)
@@ -95,6 +95,9 @@ As section headers, we use:
 Please also be sure to update the Beta/Unreleased sections at the top with the
 relevant commit ranges.
 
+If you have the time, it would be appreciated if you double-check, that the
+`#[clippy::version]` attributes for the added lints contains the correct version.
+
 [changelog]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md
 [forge]: https://forge.rust-lang.org/
 [rust_master_tools]: https://github.com/rust-lang/rust/tree/master/src/tools/clippy
index 5d858e0da46898210058e2e678697b1f56081417..61a90445d753c6b6f3d5b6be7a0ebf4b7e6d9150 100644 (file)
@@ -56,7 +56,7 @@ For more information on configuring lint levels, see the [rustc documentation].
 Clippy has lint groups which are allow-by-default. This means, that you will
 have to enable the lints in those groups manually.
 
-For a full list of all lints with their description and examples, please refere
+For a full list of all lints with their description and examples, please refer
 to [Clippy's lint list]. The two most important allow-by-default groups are
 described below:
 
diff --git a/src/tools/clippy/clippy_dev/src/dogfood.rs b/src/tools/clippy/clippy_dev/src/dogfood.rs
new file mode 100644 (file)
index 0000000..b69e9f6
--- /dev/null
@@ -0,0 +1,33 @@
+use crate::clippy_project_root;
+use std::process::Command;
+
+/// # Panics
+///
+/// Panics if unable to run the dogfood test
+pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool) {
+    let mut cmd = Command::new("cargo");
+
+    cmd.current_dir(clippy_project_root())
+        .args(["test", "--test", "dogfood"])
+        .args(["--features", "internal"])
+        .args(["--", "dogfood_clippy"]);
+
+    let mut dogfood_args = Vec::new();
+    if fix {
+        dogfood_args.push("--fix");
+    }
+
+    if allow_dirty {
+        dogfood_args.push("--allow-dirty");
+    }
+
+    if allow_staged {
+        dogfood_args.push("--allow-staged");
+    }
+
+    cmd.env("__CLIPPY_DOGFOOD_ARGS", dogfood_args.join(" "));
+
+    let output = cmd.output().expect("failed to run command");
+
+    println!("{}", String::from_utf8_lossy(&output.stdout));
+}
index fe69284ab10e422cc443dc618a5ac235b42ddc6a..8536e2429926a6b9b726b4498db635cfd64a2b1d 100644 (file)
@@ -11,6 +11,7 @@
 use std::path::PathBuf;
 
 pub mod bless;
+pub mod dogfood;
 pub mod fmt;
 pub mod lint;
 pub mod new_lint;
index 243a901503f160f617718e30e16b2242ab99ace6..a29ba2d0c85e34c4bf62da43a655381955dab95d 100644 (file)
@@ -3,7 +3,7 @@
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
 use clap::{Arg, ArgAction, ArgMatches, Command, PossibleValue};
-use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints};
+use clippy_dev::{bless, dogfood, fmt, lint, new_lint, serve, setup, update_lints};
 use indoc::indoc;
 
 fn main() {
@@ -13,6 +13,13 @@ fn main() {
         Some(("bless", matches)) => {
             bless::bless(matches.contains_id("ignore-timestamp"));
         },
+        Some(("dogfood", matches)) => {
+            dogfood::dogfood(
+                matches.contains_id("fix"),
+                matches.contains_id("allow-dirty"),
+                matches.contains_id("allow-staged"),
+            );
+        },
         Some(("fmt", matches)) => {
             fmt::run(matches.contains_id("check"), matches.contains_id("verbose"));
         },
@@ -104,6 +111,17 @@ fn get_clap_config() -> ArgMatches {
                     .long("ignore-timestamp")
                     .help("Include files updated before clippy was built"),
             ),
+            Command::new("dogfood").about("Runs the dogfood test").args([
+                Arg::new("fix").long("fix").help("Apply the suggestions when possible"),
+                Arg::new("allow-dirty")
+                    .long("allow-dirty")
+                    .help("Fix code even if the working directory has changes")
+                    .requires("fix"),
+                Arg::new("allow-staged")
+                    .long("allow-staged")
+                    .help("Fix code even if the working directory has staged changes")
+                    .requires("fix"),
+            ]),
             Command::new("fmt")
                 .about("Run rustfmt on all projects and tests")
                 .args([
index 2e0659f42d7b6b85ea248f4e45a91513c70ed2ca..c089f4d8ce4bb2c3f9757351b44fdd999f338242 100644 (file)
@@ -553,7 +553,7 @@ fn is_ident_char(c: u8) -> bool {
         pos = m.end();
     }
     result.push_str(&contents[pos..]);
-    edited.then(|| result)
+    edited.then_some(result)
 }
 
 fn round_to_fifty(count: usize) -> usize {
index eee5f90d178852368c1237a2a7c898ae78cdbee4..1761360fb2812c8fdda7afe736d07f742d309e68 100644 (file)
     ///   baz().await; // Lint violation
     /// }
     /// ```
-    #[clippy::version = "1.49.0"]
+    #[clippy::version = "1.62.0"]
     pub AWAIT_HOLDING_INVALID_TYPE,
     suspicious,
     "holding a type across an await point which is not allowed to be held as per the configuration"
index 1582ec9ee5ce6e21776768a16d4b4159c0450795..937765b66147912aa6c9172a2b61b4369f91cbec 100644 (file)
@@ -22,7 +22,7 @@
     /// ```
     /// let x = &12;
     /// let addr_x = &x as *const _ as usize;
-    /// let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggerd.
+    /// let addr_y = &&*x as *const _ as usize; // assert ok now, and lint triggered.
     ///                                         // But if we fix it, assert will fail.
     /// assert_ne!(addr_x, addr_y);
     /// ```
index 02c2f30a4dd6acb1e4b37f2cf9faa1b6bc9b50ed..af3798a0cc8c068520c2efad604a4947fb1c3079 100644 (file)
     /// let x: i32 = -42;
     /// let y: u32 = x.unsigned_abs();
     /// ```
-    #[clippy::version = "1.61.0"]
+    #[clippy::version = "1.62.0"]
     pub CAST_ABS_TO_UNSIGNED,
     suspicious,
     "casting the result of `abs()` to an unsigned integer can panic"
index 1deff9684a140abedbe5d1da5facb0f7bba1486f..0e3d9317590f3c80782c517e5c64fc66019dc40d 100644 (file)
@@ -1,13 +1,16 @@
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then};
 use clippy_utils::source::{first_line_of_span, indent_of, reindent_multiline, snippet, snippet_opt};
+use clippy_utils::ty::needs_ordered_drop;
+use clippy_utils::visitors::for_each_expr;
 use clippy_utils::{
-    eq_expr_value, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause, is_lint_allowed,
-    search_same, ContainsName, HirEqInterExpr, SpanlessEq,
+    capture_local_usage, eq_expr_value, get_enclosing_block, hash_expr, hash_stmt, if_sequence, is_else_clause,
+    is_lint_allowed, path_to_local, search_same, ContainsName, HirEqInterExpr, SpanlessEq,
 };
 use core::iter;
+use core::ops::ControlFlow;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit;
-use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Stmt, StmtKind};
+use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, HirIdSet, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::hygiene::walk_chain;
@@ -214,7 +217,7 @@ fn lint_if_same_then_else(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&
 fn lint_branches_sharing_code<'tcx>(
     cx: &LateContext<'tcx>,
     conds: &[&'tcx Expr<'_>],
-    blocks: &[&Block<'tcx>],
+    blocks: &[&'tcx Block<'_>],
     expr: &'tcx Expr<'_>,
 ) {
     // We only lint ifs with multiple blocks
@@ -340,6 +343,21 @@ fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool {
     }
 }
 
+/// Checks if the statement modifies or moves any of the given locals.
+fn modifies_any_local<'tcx>(cx: &LateContext<'tcx>, s: &'tcx Stmt<'_>, locals: &HirIdSet) -> bool {
+    for_each_expr(s, |e| {
+        if let Some(id) = path_to_local(e)
+            && locals.contains(&id)
+            && !capture_local_usage(cx, e).is_imm_ref()
+        {
+            ControlFlow::Break(())
+        } else {
+            ControlFlow::Continue(())
+        }
+    })
+    .is_some()
+}
+
 /// Checks if the given statement should be considered equal to the statement in the same position
 /// for each block.
 fn eq_stmts(
@@ -365,18 +383,52 @@ fn eq_stmts(
         .all(|b| get_stmt(b).map_or(false, |s| eq.eq_stmt(s, stmt)))
 }
 
-fn scan_block_for_eq(cx: &LateContext<'_>, _conds: &[&Expr<'_>], block: &Block<'_>, blocks: &[&Block<'_>]) -> BlockEq {
+#[expect(clippy::too_many_lines)]
+fn scan_block_for_eq<'tcx>(
+    cx: &LateContext<'tcx>,
+    conds: &[&'tcx Expr<'_>],
+    block: &'tcx Block<'_>,
+    blocks: &[&'tcx Block<'_>],
+) -> BlockEq {
     let mut eq = SpanlessEq::new(cx);
     let mut eq = eq.inter_expr();
     let mut moved_locals = Vec::new();
 
+    let mut cond_locals = HirIdSet::default();
+    for &cond in conds {
+        let _: Option<!> = for_each_expr(cond, |e| {
+            if let Some(id) = path_to_local(e) {
+                cond_locals.insert(id);
+            }
+            ControlFlow::Continue(())
+        });
+    }
+
+    let mut local_needs_ordered_drop = false;
     let start_end_eq = block
         .stmts
         .iter()
         .enumerate()
-        .find(|&(i, stmt)| !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals))
+        .find(|&(i, stmt)| {
+            if let StmtKind::Local(l) = stmt.kind
+                && needs_ordered_drop(cx, cx.typeck_results().node_type(l.hir_id))
+            {
+                local_needs_ordered_drop = true;
+                return true;
+            }
+            modifies_any_local(cx, stmt, &cond_locals)
+                || !eq_stmts(stmt, blocks, |b| b.stmts.get(i), &mut eq, &mut moved_locals)
+        })
         .map_or(block.stmts.len(), |(i, _)| i);
 
+    if local_needs_ordered_drop {
+        return BlockEq {
+            start_end_eq,
+            end_begin_eq: None,
+            moved_locals,
+        };
+    }
+
     // Walk backwards through the final expression/statements so long as their hashes are equal. Note
     // `SpanlessHash` treats all local references as equal allowing locals declared earlier in the block
     // to match those in other blocks. e.g. If each block ends with the following the hash value will be
index 9b8a481b6eab796157268e671ca3ebcfabbd0cd6..f6ec8fe7edc1914674d6e70eccf5c45711b9ad68 100644 (file)
@@ -43,7 +43,7 @@
     /// #[allow(clippy::crate_in_macro_def)]
     /// macro_rules! ok { ... crate::foo ... }
     /// ```
-    #[clippy::version = "1.61.0"]
+    #[clippy::version = "1.62.0"]
     pub CRATE_IN_MACRO_DEF,
     suspicious,
     "using `crate` in a macro definition"
index 0f4a2f79ac5d7a95c173fcf3d72593486ad324e5..8c7cf7748be1359440197d3692fb6daaf677c024 100644 (file)
@@ -8,8 +8,8 @@
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_ty, Visitor};
 use rustc_hir::{
-    self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, GenericArg, HirId, ImplItem,
-    ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
+    self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Closure, Expr, ExprKind, FnRetTy, GenericArg, HirId,
+    ImplItem, ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
     TraitItemKind, TyKind, UnOp,
 };
 use rustc_infer::infer::TyCtxtInferExt;
@@ -717,20 +717,36 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
 
             Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
                 ExprKind::Ret(_) => {
-                    let output = cx
-                        .tcx
-                        .fn_sig(cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()))
-                        .skip_binder()
-                        .output();
-                    Some(if !output.is_ref() {
-                        Position::Other(precedence)
-                    } else if output.has_placeholders() || output.has_opaque_types() {
-                        Position::ReborrowStable(precedence)
-                    } else {
-                        Position::DerefStable(precedence)
-                    })
+                    let owner_id = cx.tcx.hir().body_owner(cx.enclosing_body.unwrap());
+                    Some(
+                        if let Node::Expr(Expr {
+                            kind: ExprKind::Closure(&Closure { fn_decl, .. }),
+                            ..
+                        }) = cx.tcx.hir().get(owner_id)
+                        {
+                            match fn_decl.output {
+                                FnRetTy::Return(ty) => binding_ty_auto_deref_stability(ty, precedence),
+                                FnRetTy::DefaultReturn(_) => Position::Other(precedence),
+                            }
+                        } else {
+                            let output = cx
+                                .tcx
+                                .fn_sig(cx.tcx.hir().local_def_id(owner_id))
+                                .skip_binder()
+                                .output();
+                            if !output.is_ref() {
+                                Position::Other(precedence)
+                            } else if output.has_placeholders() || output.has_opaque_types() {
+                                Position::ReborrowStable(precedence)
+                            } else {
+                                Position::DerefStable(precedence)
+                            }
+                        },
+                    )
+                },
+                ExprKind::Call(func, _) if func.hir_id == child_id => {
+                    (child_id == e.hir_id).then_some(Position::Callee)
                 },
-                ExprKind::Call(func, _) if func.hir_id == child_id => (child_id == e.hir_id).then(|| Position::Callee),
                 ExprKind::Call(func, args) => args
                     .iter()
                     .position(|arg| arg.hir_id == child_id)
@@ -756,9 +772,14 @@ fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &
                             } else if let Some(trait_id) = cx.tcx.trait_of_item(id)
                                 && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
                                 && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
-                                && let subs = cx.typeck_results().node_substs_opt(child_id).unwrap_or_else(
-                                    || cx.tcx.mk_substs([].iter())
-                                ) && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
+                                && let subs = match cx
+                                    .typeck_results()
+                                    .node_substs_opt(parent.hir_id)
+                                    .and_then(|subs| subs.get(1..))
+                                {
+                                    Some(subs) => cx.tcx.mk_substs(subs.iter().copied()),
+                                    None => cx.tcx.mk_substs([].iter()),
+                                } && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
                                     // Trait methods taking `&self`
                                     sub_ty
                                 } else {
index 28f218a8e344f1228585e75829795a9683580018..a982990e4186c9cc6c2aef5bb9477d9a7b96651a 100644 (file)
     ///     i_am_eq_too: Vec<String>,
     /// }
     /// ```
-    #[clippy::version = "1.62.0"]
+    #[clippy::version = "1.63.0"]
     pub DERIVE_PARTIAL_EQ_WITHOUT_EQ,
     style,
     "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
index 25014bfa1a5b12595b41415a2010317ac7952143..b35f0b8ca52dafc0fda6c5cde9ddaea8fc19a9f4 100644 (file)
     /// let x = Foo;
     /// std::mem::drop(x);
     /// ```
-    #[clippy::version = "1.61.0"]
+    #[clippy::version = "1.62.0"]
     pub DROP_NON_DROP,
     suspicious,
     "call to `std::mem::drop` with a value which does not implement `Drop`"
     /// let x = Foo;
     /// std::mem::forget(x);
     /// ```
-    #[clippy::version = "1.61.0"]
+    #[clippy::version = "1.62.0"]
     pub FORGET_NON_DROP,
     suspicious,
     "call to `std::mem::forget` with a value which does not implement `Drop`"
index 4f49bb879f5035c9b66e07a354089d681046e5e9..e1eb3b6324c7820091e7b10dfa23f8c80d6e157a 100644 (file)
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind};
 use rustc_errors::MultiSpan;
-use rustc_lint::{EarlyContext, EarlyLintPass, LintContext, Level};
+use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{FileName, Span};
 use std::collections::BTreeMap;
@@ -79,21 +79,29 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
     }
 
     fn check_crate_post(&mut self, cx: &EarlyContext<'_>, _: &Crate) {
-        for Modules { local_path, spans, lint_levels } in self.modules.values() {
+        for Modules {
+            local_path,
+            spans,
+            lint_levels,
+        } in self.modules.values()
+        {
             if spans.len() < 2 {
                 continue;
             }
 
             // At this point the lint would be emitted
             assert_eq!(spans.len(), lint_levels.len());
-            let spans: Vec<_> = spans.into_iter().zip(lint_levels).filter_map(|(span, lvl)|{
-                if let Some(id) = lvl.get_expectation_id() {
-                    cx.fulfill_expectation(id);
-                }
+            let spans: Vec<_> = spans
+                .iter()
+                .zip(lint_levels)
+                .filter_map(|(span, lvl)| {
+                    if let Some(id) = lvl.get_expectation_id() {
+                        cx.fulfill_expectation(id);
+                    }
 
-                (!matches!(lvl, Level::Allow | Level::Expect(_))).then_some(*span)
-            })
-            .collect();
+                    (!matches!(lvl, Level::Allow | Level::Expect(_))).then_some(*span)
+                })
+                .collect();
 
             if spans.len() < 2 {
                 continue;
index 325ae2356c14cb255531b3df3950d3208feaa39e..ec063c0f777e3759313cbc8ae7faf745385d8784 100644 (file)
@@ -26,7 +26,7 @@
     /// ```rust
     /// struct S;
     /// ```
-    #[clippy::version = "1.61.0"]
+    #[clippy::version = "1.62.0"]
     pub EMPTY_DROP,
     restriction,
     "empty `Drop` implementations"
index 27743a0ebec7e59dc688de5547043a4b2b60e95b..4e3ae4c9614113f3a1132462d8b70600c8a7f8c9 100644 (file)
@@ -650,7 +650,7 @@ fn find_insert_calls<'tcx>(
     let allow_insert_closure = s.allow_insert_closure;
     let is_single_insert = s.is_single_insert;
     let edits = s.edits;
-    s.can_use_entry.then(|| InsertSearchResults {
+    s.can_use_entry.then_some(InsertSearchResults {
         edits,
         allow_insert_closure,
         is_single_insert,
index ef1216358dd97eb8eaa6dd00ddfbf3983c056e85..fdfb821ac7895becb9fa97f56463dad42f0b7632 100644 (file)
@@ -4,7 +4,8 @@
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Pat, PatKind};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::Ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
@@ -67,6 +68,7 @@ fn is_structural_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: T
 impl<'tcx> LateLintPass<'tcx> for PatternEquality {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if_chain! {
+            if !in_external_macro(cx.sess(), expr.span);
             if let ExprKind::Let(let_expr) = expr.kind;
             if unary_pattern(let_expr.pat);
             let exp_ty = cx.typeck_results().expr_ty(let_expr.init);
index ee15ae9f59aca8eb96429d52464b3c53ae689a5b..ebf5ab086dcea7ae6966984ad8810a47c39fcf3a 100644 (file)
     /// ### Why is this bad?
     /// Introduces an extra, avoidable heap allocation.
     ///
+    /// ### Known problems
+    /// `format!` returns a `String` but `write!` returns a `Result`.
+    /// Thus you are forced to ignore the `Err` variant to achieve the same API.
+    ///
+    /// While using `write!` in the suggested way should never fail, this isn't necessarily clear to the programmer.
+    ///
     /// ### Example
     /// ```rust
     /// let mut s = String::new();
@@ -27,9 +33,9 @@
     /// let mut s = String::new();
     /// let _ = write!(s, "0x{:X}", 1024);
     /// ```
-    #[clippy::version = "1.61.0"]
+    #[clippy::version = "1.62.0"]
     pub FORMAT_PUSH_STRING,
-    perf,
+    restriction,
     "`format!(..)` appended to existing `String`"
 }
 declare_lint_pass!(FormatPushString => [FORMAT_PUSH_STRING]);
index 6a031a627df946669e206ae77fb2255d21bf71e4..c5abcc462545c935fb8a6b37e678685ce7bf639b 100644 (file)
@@ -127,7 +127,7 @@ fn get_impl_span(cx: &LateContext<'_>, id: LocalDefId) -> Option<Span> {
         (!span.from_expansion()
             && impl_item.generics.params.is_empty()
             && !is_lint_allowed(cx, MULTIPLE_INHERENT_IMPL, id))
-        .then(|| span)
+        .then_some(span)
     } else {
         None
     }
diff --git a/src/tools/clippy/clippy_lints/src/invalid_utf8_in_unchecked.rs b/src/tools/clippy/clippy_lints/src/invalid_utf8_in_unchecked.rs
new file mode 100644 (file)
index 0000000..e0a607f
--- /dev/null
@@ -0,0 +1,74 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{match_function_call, paths};
+use rustc_ast::{BorrowKind, LitKind};
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::source_map::Spanned;
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `std::str::from_utf8_unchecked` with an invalid UTF-8 literal
+    ///
+    /// ### Why is this bad?
+    /// Creating such a `str` would result in undefined behavior
+    ///
+    /// ### Example
+    /// ```rust
+    /// # #[allow(unused)]
+    /// unsafe {
+    ///     std::str::from_utf8_unchecked(b"cl\x82ippy");
+    /// }
+    /// ```
+    #[clippy::version = "1.64.0"]
+    pub INVALID_UTF8_IN_UNCHECKED,
+    correctness,
+    "using a non UTF-8 literal in `std::std::from_utf8_unchecked`"
+}
+declare_lint_pass!(InvalidUtf8InUnchecked => [INVALID_UTF8_IN_UNCHECKED]);
+
+impl<'tcx> LateLintPass<'tcx> for InvalidUtf8InUnchecked {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        if let Some([arg]) = match_function_call(cx, expr, &paths::STR_FROM_UTF8_UNCHECKED) {
+            match &arg.kind {
+                ExprKind::Lit(Spanned { node: lit, .. }) => {
+                    if let LitKind::ByteStr(bytes) = &lit
+                        && std::str::from_utf8(bytes).is_err()
+                    {
+                        lint(cx, expr.span);
+                    }
+                },
+                ExprKind::AddrOf(BorrowKind::Ref, _, Expr { kind: ExprKind::Array(args), .. }) => {
+                    let elements = args.iter().map(|e|{
+                        match &e.kind {
+                            ExprKind::Lit(Spanned { node: lit, .. }) => match lit {
+                                LitKind::Byte(b) => Some(*b),
+                                #[allow(clippy::cast_possible_truncation)]
+                                LitKind::Int(b, _) => Some(*b as u8),
+                                _ => None
+                            }
+                            _ => None
+                        }
+                    }).collect::<Option<Vec<_>>>();
+
+                    if let Some(elements) = elements
+                        && std::str::from_utf8(&elements).is_err()
+                    {
+                        lint(cx, expr.span);
+                    }
+                }
+                _ => {}
+            }
+        }
+    }
+}
+
+fn lint(cx: &LateContext<'_>, span: Span) {
+    span_lint(
+        cx,
+        INVALID_UTF8_IN_UNCHECKED,
+        span,
+        "non UTF-8 literal in `std::str::from_utf8_unchecked`",
+    );
+}
index 9be057bcf901f5255564173d5b8ea446b800cd61..c58df126d62442a056d63437518699c3d189012c 100644 (file)
@@ -30,7 +30,7 @@
     /// For types that implement `Copy`, the suggestion to `Box` a variant's
     /// data would require removing the trait impl. The types can of course
     /// still be `Clone`, but that is worse ergonomically. Depending on the
-    /// use case it may be possible to store the large data in an auxillary
+    /// use case it may be possible to store the large data in an auxiliary
     /// structure (e.g. Arena or ECS).
     ///
     /// The lint will ignore generic types if the layout depends on the
index 563ad891603a7b74882191c203f72da3fedf2c34..9afc714b11ca1613c2dea5936e022703707b1515 100644 (file)
@@ -71,7 +71,6 @@
     LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
     LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
     LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
-    LintId::of(format_push_string::FORMAT_PUSH_STRING),
     LintId::of(formatting::POSSIBLE_MISSING_COMMA),
     LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
     LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
@@ -92,6 +91,7 @@
     LintId::of(init_numbered_fields::INIT_NUMBERED_FIELDS),
     LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
     LintId::of(int_plus_one::INT_PLUS_ONE),
+    LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED),
     LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
     LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
     LintId::of(len_zero::COMPARISON_TO_EMPTY),
index 7d5e65cb27a1f709177d69e5b2105d16a69cb581..9975859c54fea5a7bd676730d8b2d24cf58b5385 100644 (file)
@@ -29,6 +29,7 @@
     LintId::of(infinite_iter::INFINITE_ITER),
     LintId::of(inherent_to_string::INHERENT_TO_STRING_SHADOW_DISPLAY),
     LintId::of(inline_fn_without_body::INLINE_FN_WITHOUT_BODY),
+    LintId::of(invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED),
     LintId::of(let_underscore::LET_UNDERSCORE_LOCK),
     LintId::of(literal_representation::MISTYPED_LITERAL_SUFFIXES),
     LintId::of(loops::ITER_NEXT_LOOP),
index d3c75f8b519107654df8d992bb677017deb594a1..91d27bf526d096674e2dd1b6e8323c97725eb35e 100644 (file)
     inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
     int_plus_one::INT_PLUS_ONE,
     invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
+    invalid_utf8_in_unchecked::INVALID_UTF8_IN_UNCHECKED,
     items_after_statements::ITEMS_AFTER_STATEMENTS,
     iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
     large_const_arrays::LARGE_CONST_ARRAYS,
     size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT,
     slow_vector_initialization::SLOW_VECTOR_INITIALIZATION,
     stable_sort_primitive::STABLE_SORT_PRIMITIVE,
+    std_instead_of_core::ALLOC_INSTEAD_OF_CORE,
+    std_instead_of_core::STD_INSTEAD_OF_ALLOC,
+    std_instead_of_core::STD_INSTEAD_OF_CORE,
     strings::STRING_ADD,
     strings::STRING_ADD_ASSIGN,
     strings::STRING_FROM_UTF8_AS_BYTES,
index 6bf519c24e848ad2272d6f11e48b1e5166a32596..e1b90acb93c2e9ae78baa4778790830996eb5065 100644 (file)
@@ -7,7 +7,6 @@
     LintId::of(escape::BOXED_LOCAL),
     LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
     LintId::of(format_args::TO_STRING_IN_FORMAT_ARGS),
-    LintId::of(format_push_string::FORMAT_PUSH_STRING),
     LintId::of(large_const_arrays::LARGE_CONST_ARRAYS),
     LintId::of(large_enum_variant::LARGE_ENUM_VARIANT),
     LintId::of(loops::MANUAL_MEMCPY),
index 970e9db4772cb192eaf80ba336d213f92061f6e7..43f1c892eb9b9c396db40f9deb83d481d380c7da 100644 (file)
@@ -21,6 +21,7 @@
     LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
     LintId::of(exit::EXIT),
     LintId::of(float_literal::LOSSY_FLOAT_LITERAL),
+    LintId::of(format_push_string::FORMAT_PUSH_STRING),
     LintId::of(if_then_some_else_none::IF_THEN_SOME_ELSE_NONE),
     LintId::of(implicit_return::IMPLICIT_RETURN),
     LintId::of(indexing_slicing::INDEXING_SLICING),
@@ -65,6 +66,9 @@
     LintId::of(shadow::SHADOW_SAME),
     LintId::of(shadow::SHADOW_UNRELATED),
     LintId::of(single_char_lifetime_names::SINGLE_CHAR_LIFETIME_NAMES),
+    LintId::of(std_instead_of_core::ALLOC_INSTEAD_OF_CORE),
+    LintId::of(std_instead_of_core::STD_INSTEAD_OF_ALLOC),
+    LintId::of(std_instead_of_core::STD_INSTEAD_OF_CORE),
     LintId::of(strings::STRING_ADD),
     LintId::of(strings::STRING_SLICE),
     LintId::of(strings::STRING_TO_STRING),
index 884baaea0292693193dbaa12edcaada6968824c6..1988c24578e0b781ea57caf881d987f4ef568d62 100644 (file)
@@ -255,6 +255,7 @@ macro_rules! declare_clippy_lint {
 mod inline_fn_without_body;
 mod int_plus_one;
 mod invalid_upcast_comparisons;
+mod invalid_utf8_in_unchecked;
 mod items_after_statements;
 mod iter_not_returning_iterator;
 mod large_const_arrays;
@@ -364,6 +365,7 @@ macro_rules! declare_clippy_lint {
 mod size_of_in_element_count;
 mod slow_vector_initialization;
 mod stable_sort_primitive;
+mod std_instead_of_core;
 mod strings;
 mod strlen_on_c_strings;
 mod suspicious_operation_groupings;
@@ -913,6 +915,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || Box::new(manual_retain::ManualRetain::new(msrv)));
     let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
     store.register_late_pass(move || Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
+    store.register_late_pass(|| Box::new(invalid_utf8_in_unchecked::InvalidUtf8InUnchecked));
+    store.register_late_pass(|| Box::new(std_instead_of_core::StdReexports));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
index fc50e8addccec8f84e87c03fffedd10a92daa3dd..8e3ab26a947f1c9413e1e06233696a2c075448c3 100644 (file)
@@ -34,7 +34,8 @@ pub(super) fn check<'tcx>(
                 if let Some((name, ty, initializer)) = initialize_visitor.get_result();
                 if is_integer_const(cx, initializer, 0);
                 then {
-                    let mut applicability = Applicability::MachineApplicable;
+                    let mut applicability = Applicability::MaybeIncorrect;
+                    let span = expr.span.with_hi(arg.span.hi());
 
                     let int_name = match ty.map(Ty::kind) {
                         // usize or inferred
@@ -42,7 +43,7 @@ pub(super) fn check<'tcx>(
                             span_lint_and_sugg(
                                 cx,
                                 EXPLICIT_COUNTER_LOOP,
-                                expr.span.with_hi(arg.span.hi()),
+                                span,
                                 &format!("the variable `{}` is used as a loop counter", name),
                                 "consider using",
                                 format!(
@@ -63,11 +64,11 @@ pub(super) fn check<'tcx>(
                     span_lint_and_then(
                         cx,
                         EXPLICIT_COUNTER_LOOP,
-                        expr.span.with_hi(arg.span.hi()),
+                        span,
                         &format!("the variable `{}` is used as a loop counter", name),
                         |diag| {
                             diag.span_suggestion(
-                                expr.span.with_hi(arg.span.hi()),
+                                span,
                                 "consider using",
                                 format!(
                                     "for ({}, {}) in (0_{}..).zip({})",
index 33736d6d4e650691e6f9a394b2d2f7b8def212e0..215c83a7edf6e9f91d86ecc5ce962894ad71ffa6 100644 (file)
@@ -139,7 +139,7 @@ fn extract<'tcx>(block: &Block<'tcx>) -> Option<(&'tcx Stmt<'tcx>, &'tcx Expr<'t
     if_chain! {
         // This should be the loop
         if let Some((node_hir, Node::Stmt(..))) = parent_iter.next();
-        // This should be the funciton body
+        // This should be the function body
         if let Some((_, Node::Block(block))) = parent_iter.next();
         if let Some((last_stmt, last_ret)) = extract(block);
         if last_stmt.hir_id == node_hir;
index d276c901059974a54c38d51601f26c3a6d070638..1d6ddf4b99f7b6835717672b238eb660547a4e45 100644 (file)
@@ -51,22 +51,32 @@ pub(super) fn check<'tcx>(
                 _ => ""
             };
 
+            let sugg = format!("{arg_snippet}{copied}.flatten()");
+
+            // If suggestion is not a one-liner, it won't be shown inline within the error message. In that case,
+            // it will be shown in the extra `help` message at the end, which is why the first `help_msg` needs
+            // to refer to the correct relative position of the suggestion.
+            let help_msg = if sugg.contains('\n') {
+                "remove the `if let` statement in the for loop and then..."
+            } else {
+                "...and remove the `if let` statement in the for loop"
+            };
+
             span_lint_and_then(
                 cx,
                 MANUAL_FLATTEN,
                 span,
                 &msg,
                 |diag| {
-                    let sugg = format!("{}{}.flatten()", arg_snippet, copied);
                     diag.span_suggestion(
                         arg.span,
                         "try",
                         sugg,
-                        Applicability::MaybeIncorrect,
+                        applicability,
                     );
                     diag.span_help(
                         inner_expr.span,
-                        "...and remove the `if let` statement in the for loop",
+                        help_msg,
                     );
                 }
             );
index b94bbd2bd417de88fe6293b832cc79183cda7130..e9e215e662f1939c6d799d5e0cdd84aee65fdea5 100644 (file)
@@ -3,13 +3,15 @@
 use clippy_utils::higher;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{
-    get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used,
+    get_enclosing_loop_or_multi_call_closure, is_refutable, is_trait_method, match_def_path, paths,
+    visitors::is_res_used,
 };
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::{Closure, def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
+use rustc_hir::{def::Res, Closure, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
 use rustc_lint::LateContext;
+use rustc_middle::hir::nested_filter::OnlyBodies;
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_span::{symbol::sym, Symbol};
 
@@ -249,6 +251,11 @@ struct AfterLoopVisitor<'a, 'b, 'tcx> {
         used_iter: bool,
     }
     impl<'tcx> Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> {
+        type NestedFilter = OnlyBodies;
+        fn nested_visit_map(&mut self) -> Self::Map {
+            self.cx.tcx.hir()
+        }
+
         fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
             if self.used_iter {
                 return;
@@ -283,6 +290,11 @@ struct NestedLoopVisitor<'a, 'b, 'tcx> {
         used_after: bool,
     }
     impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
+        type NestedFilter = OnlyBodies;
+        fn nested_visit_map(&mut self) -> Self::Map {
+            self.cx.tcx.hir()
+        }
+
         fn visit_local(&mut self, l: &'tcx Local<'_>) {
             if !self.after_loop {
                 l.pat.each_binding_or_first(&mut |_, id, _, _| {
@@ -320,10 +332,7 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
         }
     }
 
-    if let Some(e) = get_enclosing_loop_or_closure(cx.tcx, loop_expr) {
-        // The iterator expression will be used on the next iteration (for loops), or on the next call (for
-        // closures) unless it is declared within the enclosing expression. TODO: Check for closures
-        // used where an `FnOnce` type is expected.
+    if let Some(e) = get_enclosing_loop_or_multi_call_closure(cx, loop_expr) {
         let local_id = match iter_expr.path {
             Res::Local(id) => id,
             _ => return true,
index 93a34f452f6d67d8ee79c25568ce62484e4f733c..945880d21471b2a31af15ee8f53ae573174967d7 100644 (file)
@@ -6,8 +6,8 @@
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
-    AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound, HirId,
-    IsAsync, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind,
+    AsyncGeneratorKind, Block, Body, Closure, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericArg, GenericBound,
+    HirId, IsAsync, ItemKind, LifetimeName, Term, TraitRef, Ty, TyKind, TypeBindingKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
index 4278e98dc91facd694f9ad7d64d01d12a639ad4a..2b04475c7a9debd962534a28343034ad1a5af815 100644 (file)
@@ -161,7 +161,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
                 (matches!(v.data, hir::VariantData::Unit(_))
                     && v.ident.as_str().starts_with('_')
                     && is_doc_hidden(cx.tcx.hir().attrs(v.id)))
-                .then(|| (id, v.span))
+                .then_some((id, v.span))
             });
             if let Some((id, span)) = iter.next()
                 && iter.next().is_none()
index 2ce9d0e77c1fd167f093fd0cb2613bfaf9bab27b..95cc6bdbd8ba7bf0b748b7d9559bb9dc7e8af8f2 100644 (file)
@@ -71,7 +71,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             && let Some(const3) = check_for_unsigned_int_constant(cx, right)
             // Also ensures the const is nonzero since zero can't be a divisor
             && const1 == const2 && const2 == const3
-            && let Some(hir_id) = path_to_local(expr3) {
+            && let Some(hir_id) = path_to_local(expr3)
+            && let Some(Node::Pat(_)) = cx.tcx.hir().find(hir_id) {
                 // Apply only to params or locals with annotated types
                 match cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
                     Some(Node::Param(..)) => (),
index 542905a2d763cffa4879b4f75d7acefa3e4aa7a3..8f98b43b9e5c8d240327b269a2961468d0e28275 100644 (file)
@@ -105,7 +105,7 @@ fn check<'tcx>(
 
     // Determine which binding mode to use.
     let explicit_ref = some_pat.contains_explicit_ref_binding();
-    let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability));
+    let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then_some(ty_mutability));
 
     let as_ref_str = match binding_ref {
         Some(Mutability::Mut) => ".as_mut()",
index a68eec842abc51a08418e793c9b670e93e5b45a6..0da4833f1dfe0c9dfc5694874ab0cad257c30487 100644 (file)
@@ -81,14 +81,14 @@ fn find_matches_sugg<'a, 'b, I>(
         if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
         let iter_without_last = iter.clone();
         if let Some((first_attrs, _, first_expr, first_guard)) = iter.next();
-        if let Some(b0) = find_bool_lit(&first_expr.kind, is_if_let);
-        if let Some(b1) = find_bool_lit(&last_expr.kind, is_if_let);
+        if let Some(b0) = find_bool_lit(&first_expr.kind);
+        if let Some(b1) = find_bool_lit(&last_expr.kind);
         if b0 != b1;
         if first_guard.is_none() || iter.len() == 0;
         if first_attrs.is_empty();
         if iter
             .all(|arm| {
-                find_bool_lit(&arm.2.kind, is_if_let).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
+                find_bool_lit(&arm.2.kind).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
             });
         then {
             if let Some(last_pat) = last_pat_opt {
@@ -144,7 +144,7 @@ fn find_matches_sugg<'a, 'b, I>(
 }
 
 /// Extract a `bool` or `{ bool }`
-fn find_bool_lit(ex: &ExprKind<'_>, is_if_let: bool) -> Option<bool> {
+fn find_bool_lit(ex: &ExprKind<'_>) -> Option<bool> {
     match ex {
         ExprKind::Lit(Spanned {
             node: LitKind::Bool(b), ..
@@ -156,7 +156,7 @@ fn find_bool_lit(ex: &ExprKind<'_>, is_if_let: bool) -> Option<bool> {
                 ..
             },
             _,
-        ) if is_if_let => {
+        ) => {
             if let ExprKind::Lit(Spanned {
                 node: LitKind::Bool(b), ..
             }) = exp.kind
index 15513de7d860d90dda22a8b7acaf4fcd5c8ec0be..582782f245fcc5382ef06a3695b4d73c0316f579 100644 (file)
@@ -38,7 +38,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
             normalized_pats[i + 1..]
                 .iter()
                 .enumerate()
-                .find_map(|(j, other)| pat.has_overlapping_values(other).then(|| i + 1 + j))
+                .find_map(|(j, other)| pat.has_overlapping_values(other).then_some(i + 1 + j))
                 .unwrap_or(normalized_pats.len())
         })
         .collect();
@@ -55,7 +55,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
                 .zip(forwards_blocking_idxs[..i].iter().copied().rev())
                 .skip_while(|&(_, forward_block)| forward_block > i)
                 .find_map(|((j, other), forward_block)| {
-                    (forward_block == i || pat.has_overlapping_values(other)).then(|| j)
+                    (forward_block == i || pat.has_overlapping_values(other)).then_some(j)
                 })
                 .unwrap_or(0)
         })
@@ -365,7 +365,7 @@ fn has_overlapping_values(&self, other: &Self) -> bool {
             (Self::Slice(pats, None), Self::Slice(front, Some(back)))
             | (Self::Slice(front, Some(back)), Self::Slice(pats, None)) => {
                 // Here `pats` is an exact size match. If the combined lengths of `front` and `back` are greater
-                // then the minium length required will be greater than the length of `pats`.
+                // then the minimum length required will be greater than the length of `pats`.
                 if pats.len() < front.len() + back.len() {
                     return false;
                 }
index 3077b999f4ee445f72716412911a1238eb2d4276..d55082c66dc864a4fa4a1254a082c1fbd639512a 100644 (file)
@@ -1062,7 +1062,7 @@ fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, ar
     let start = scrutinee_span.hi();
     let mut arm_spans = arms.iter().map(|arm| {
         let data = arm.span.data();
-        (data.ctxt == SyntaxContext::root()).then(|| (data.lo, data.hi))
+        (data.ctxt == SyntaxContext::root()).then_some((data.lo, data.hi))
     });
     let end = e.span.hi();
 
@@ -1096,7 +1096,7 @@ fn contains_cfg_arm(cx: &LateContext<'_>, e: &Expr<'_>, scrutinee: &Expr<'_>, ar
             parent: None,
         }
         .span();
-        (!span_contains_cfg(cx, span)).then(|| next_start).ok_or(())
+        (!span_contains_cfg(cx, span)).then_some(next_start).ok_or(())
     });
     match found {
         Ok(start) => {
index 0704a5af52595ce158e7760b7ec8975722de8d2c..b0b15b3f54cd8b35f253218596c49c61aebc27be 100644 (file)
@@ -89,6 +89,10 @@ fn has_significant_drop_in_scrutinee<'tcx, 'a>(
     source: MatchSource,
 ) -> Option<(Vec<FoundSigDrop>, &'static str)> {
     let mut helper = SigDropHelper::new(cx);
+    let scrutinee = match (source, &scrutinee.kind) {
+        (MatchSource::ForLoopDesugar, ExprKind::Call(_, [e])) => e,
+        _ => scrutinee,
+    };
     helper.find_sig_drop(scrutinee).map(|drops| {
         let message = if source == MatchSource::Normal {
             "temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression"
index 7dbfd95c50db40d1e0215690e6eff7d1f7ca942f..692e22a7c5cf7138dbbc0f7cf6df0f06228721b3 100644 (file)
@@ -8,6 +8,7 @@
 use rustc_hir::def::Res;
 use rustc_hir::{Closure, Expr, ExprKind, PatKind, PathSegment, QPath, UnOp};
 use rustc_lint::LateContext;
+use rustc_middle::ty::adjustment::Adjust;
 use rustc_span::source_map::Span;
 use rustc_span::symbol::{sym, Symbol};
 use std::borrow::Cow;
@@ -49,35 +50,18 @@ fn is_option_filter_map<'tcx>(cx: &LateContext<'tcx>, filter_arg: &hir::Expr<'_>
     is_method(cx, map_arg, sym::unwrap) && is_method(cx, filter_arg, sym!(is_some))
 }
 
-/// lint use of `filter().map()` for `Iterators`
-fn lint_filter_some_map_unwrap(
+/// is `filter(|x| x.is_some()).map(|x| x.unwrap())`
+fn is_filter_some_map_unwrap(
     cx: &LateContext<'_>,
     expr: &hir::Expr<'_>,
     filter_recv: &hir::Expr<'_>,
     filter_arg: &hir::Expr<'_>,
     map_arg: &hir::Expr<'_>,
-    target_span: Span,
-    methods_span: Span,
-) {
+) -> bool {
     let iterator = is_trait_method(cx, expr, sym::Iterator);
     let option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(filter_recv), sym::Option);
-    if (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg) {
-        let msg = "`filter` for `Some` followed by `unwrap`";
-        let help = "consider using `flatten` instead";
-        let sugg = format!(
-            "{}",
-            reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, target_span),)
-        );
-        span_lint_and_sugg(
-            cx,
-            OPTION_FILTER_MAP,
-            methods_span,
-            msg,
-            help,
-            sugg,
-            Applicability::MachineApplicable,
-        );
-    }
+
+    (iterator || option) && is_option_filter_map(cx, filter_arg, map_arg)
 }
 
 /// lint use of `filter().map()` or `find().map()` for `Iterators`
@@ -93,15 +77,20 @@ pub(super) fn check<'tcx>(
     map_span: Span,
     is_find: bool,
 ) {
-    lint_filter_some_map_unwrap(
-        cx,
-        expr,
-        filter_recv,
-        filter_arg,
-        map_arg,
-        map_span,
-        filter_span.with_hi(expr.span.hi()),
-    );
+    if is_filter_some_map_unwrap(cx, expr, filter_recv, filter_arg, map_arg) {
+        span_lint_and_sugg(
+            cx,
+            OPTION_FILTER_MAP,
+            filter_span.with_hi(expr.span.hi()),
+            "`filter` for `Some` followed by `unwrap`",
+            "consider using `flatten` instead",
+            reindent_multiline(Cow::Borrowed("flatten()"), true, indent_of(cx, map_span)).into_owned(),
+            Applicability::MachineApplicable,
+        );
+
+        return;
+    }
+
     if_chain! {
             if is_trait_method(cx, map_recv, sym::Iterator);
 
@@ -118,7 +107,7 @@ pub(super) fn check<'tcx>(
             // closure ends with is_some() or is_ok()
             if let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind;
             if let ExprKind::MethodCall(path, [filter_arg], _) = filter_body.value.kind;
-            if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).ty_adt_def();
+            if let Some(opt_ty) = cx.typeck_results().expr_ty(filter_arg).peel_refs().ty_adt_def();
             if let Some(is_result) = if cx.tcx.is_diagnostic_item(sym::Option, opt_ty.did()) {
                 Some(false)
             } else if cx.tcx.is_diagnostic_item(sym::Result, opt_ty.did()) {
@@ -137,6 +126,19 @@ pub(super) fn check<'tcx>(
             if let ExprKind::MethodCall(seg, [map_arg, ..], _) = map_body.value.kind;
             if matches!(seg.ident.name, sym::expect | sym::unwrap | sym::unwrap_or);
 
+            // .filter(..).map(|y| f(y).copied().unwrap())
+            //                     ~~~~
+            let map_arg_peeled = match map_arg.kind {
+                ExprKind::MethodCall(method, [original_arg], _) if acceptable_methods(method) => {
+                    original_arg
+                },
+                _ => map_arg,
+            };
+
+            // .filter(|x| x.is_some()).map(|y| y[.acceptable_method()].unwrap())
+            let simple_equal = path_to_local_id(filter_arg, filter_param_id)
+                && path_to_local_id(map_arg_peeled, map_param_id);
+
             let eq_fallback = |a: &Expr<'_>, b: &Expr<'_>| {
                 // in `filter(|x| ..)`, replace `*x` with `x`
                 let a_path = if_chain! {
@@ -145,25 +147,12 @@ pub(super) fn check<'tcx>(
                     then { expr_path } else { a }
                 };
                 // let the filter closure arg and the map closure arg be equal
-                if_chain! {
-                    if path_to_local_id(a_path, filter_param_id);
-                    if path_to_local_id(b, map_param_id);
-                    if cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b);
-                    then {
-                        return true;
-                    }
-                }
-                false
-            };
-
-            if match map_arg.kind {
-                ExprKind::MethodCall(method, [original_arg], _) => {
-                    acceptable_methods(method)
-                        && SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, original_arg)
-                },
-                _ => SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg)
+                path_to_local_id(a_path, filter_param_id)
+                    && path_to_local_id(b, map_param_id)
+                    && cx.typeck_results().expr_ty_adjusted(a) == cx.typeck_results().expr_ty_adjusted(b)
             };
 
+            if simple_equal || SpanlessEq::new(cx).expr_fallback(eq_fallback).eq_expr(filter_arg, map_arg_peeled);
             then {
                 let span = filter_span.with_hi(expr.span.hi());
                 let (filter_name, lint) = if is_find {
@@ -171,10 +160,22 @@ pub(super) fn check<'tcx>(
                 } else {
                     ("filter", MANUAL_FILTER_MAP)
                 };
-                let msg = format!("`{}(..).map(..)` can be simplified as `{0}_map(..)`", filter_name);
-                let to_opt = if is_result { ".ok()" } else { "" };
-                let sugg = format!("{}_map(|{}| {}{})", filter_name, map_param_ident,
-                    snippet(cx, map_arg.span, ".."), to_opt);
+                let msg = format!("`{filter_name}(..).map(..)` can be simplified as `{filter_name}_map(..)`");
+                let (to_opt, deref) = if is_result {
+                    (".ok()", String::new())
+                } else {
+                    let derefs = cx.typeck_results()
+                        .expr_adjustments(map_arg)
+                        .iter()
+                        .filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
+                        .count();
+
+                    ("", "*".repeat(derefs))
+                };
+                let sugg = format!(
+                    "{filter_name}_map(|{map_param_ident}| {deref}{}{to_opt})",
+                    snippet(cx, map_arg.span, ".."),
+                );
                 span_lint_and_sugg(cx, lint, span, &msg, "try", sugg, Applicability::MachineApplicable);
             }
     }
index 68a75667914aa0f8f6ab34c80d408ba3a812741a..46d2fc493f81e19a5f6ba149a21ff150a75c0d8e 100644 (file)
@@ -43,7 +43,7 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<RepeatKind> {
             Some(RepeatKind::String)
         } else {
             let ty = ty.peel_refs();
-            (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)).then(|| RepeatKind::String)
+            (ty.is_str() || is_type_diagnostic_item(cx, ty, sym::String)).then_some(RepeatKind::String)
         }
     }
 }
index 9bb7bb7a7aba468e53abd72274fa654340965bc3..6981b4a66318ed1f88de71bb6365ae9e82a5499c 100644 (file)
     /// let x: Result<u32, &str> = Ok(10);
     /// x.expect_err("Testing expect_err");
     /// ```
-    #[clippy::version = "1.61.0"]
+    #[clippy::version = "1.62.0"]
     pub ERR_EXPECT,
     style,
     r#"using `.err().expect("")` when `.expect_err("")` can be used"#
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Finds usages of [`char::is_digit`]
-    /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
-    /// can be replaced with [`is_ascii_digit`]
-    /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
-    /// [`is_ascii_hexdigit`]
-    /// (https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
+    /// Finds usages of [`char::is_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_digit) that
+    /// can be replaced with [`is_ascii_digit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_digit) or
+    /// [`is_ascii_hexdigit`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_ascii_hexdigit).
     ///
     /// ### Why is this bad?
     /// `is_digit(..)` is slower and requires specifying the radix.
     /// c.is_ascii_digit();
     /// c.is_ascii_hexdigit();
     /// ```
-    #[clippy::version = "1.61.0"]
+    #[clippy::version = "1.62.0"]
     pub IS_DIGIT_ASCII_RADIX,
     style,
     "use of `char::is_digit(..)` with literal radix of 10 or 16"
 }
 
 declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for calling `take` function after `as_ref`.
     ///
     /// ### Why is this bad?
+    /// Redundant code. `take` writes `None` to its argument.
+    /// In this case the modification is useless as it's a temporary that cannot be read from afterwards.
     ///
     /// ### Example
     /// ```rust
     /// let x = Some(3);
     /// x.as_ref();
     /// ```
-    #[clippy::version = "1.61.0"]
+    #[clippy::version = "1.62.0"]
     pub NEEDLESS_OPTION_TAKE,
     complexity,
     "using `.as_ref().take()` on a temporary value"
@@ -2740,6 +2741,12 @@ fn check_methods<'tcx>(&self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     }
                 },
                 ("take", []) => needless_option_take::check(cx, expr, recv),
+                ("then", [arg]) => {
+                    if !meets_msrv(self.msrv, msrvs::BOOL_THEN_SOME) {
+                        return;
+                    }
+                    unnecessary_lazy_eval::check(cx, expr, recv, arg, "then_some");
+                },
                 ("to_os_string" | "to_owned" | "to_path_buf" | "to_vec", []) => {
                     implicit_clone::check(cx, name, expr, recv);
                 },
index 3d1208824fa34c10e187f873322b462746105104..6af134019a472956958a698163b7837c6a5b9ea3 100644 (file)
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::eager_or_lazy::switch_to_lazy_eval;
-use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite};
+use clippy_utils::source::{snippet, snippet_with_macro_callsite};
 use clippy_utils::ty::{implements_trait, match_type};
 use clippy_utils::{contains_return, is_trait_item, last_path_segment, paths};
 use if_chain::if_chain;
@@ -28,10 +28,10 @@ fn check_unwrap_or_default(
         cx: &LateContext<'_>,
         name: &str,
         fun: &hir::Expr<'_>,
-        self_expr: &hir::Expr<'_>,
         arg: &hir::Expr<'_>,
         or_has_args: bool,
         span: Span,
+        method_span: Span,
     ) -> bool {
         let is_default_default = || is_trait_item(cx, fun, sym::Default);
 
@@ -52,24 +52,14 @@ fn check_unwrap_or_default(
                 || (matches!(path, sym::new) && implements_default(arg, default_trait_id));
 
             then {
-                let mut applicability = Applicability::MachineApplicable;
-                let hint = "unwrap_or_default()";
-                let sugg_span = span;
-
-                let sugg: String = format!(
-                    "{}.{}",
-                    snippet_with_applicability(cx, self_expr.span, "..", &mut applicability),
-                    hint
-                );
-
                 span_lint_and_sugg(
                     cx,
                     OR_FUN_CALL,
-                    sugg_span,
+                    method_span.with_hi(span.hi()),
                     &format!("use of `{}` followed by a call to `{}`", name, path),
                     "try this",
-                    sugg,
-                    applicability,
+                    "unwrap_or_default()".to_string(),
+                    Applicability::MachineApplicable,
                 );
 
                 true
@@ -171,7 +161,7 @@ fn check_general_case<'tcx>(
         match inner_arg.kind {
             hir::ExprKind::Call(fun, or_args) => {
                 let or_has_args = !or_args.is_empty();
-                if !check_unwrap_or_default(cx, name, fun, self_arg, arg, or_has_args, expr.span) {
+                if !check_unwrap_or_default(cx, name, fun, arg, or_has_args, expr.span, method_span) {
                     let fun_span = if or_has_args { None } else { Some(fun.span) };
                     check_general_case(cx, name, method_span, self_arg, arg, expr.span, fun_span);
                 }
index 21767d74c87b8cb9ba2781ba6daaae980a4d4933..1876c7fb9d0510fd855b52232bdfa38b6e6e7a65 100644 (file)
@@ -20,8 +20,9 @@ pub(super) fn check<'tcx>(
 ) {
     let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
     let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
+    let is_bool = cx.typeck_results().expr_ty(recv).is_bool();
 
-    if is_option || is_result {
+    if is_option || is_result || is_bool {
         if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind {
             let body = cx.tcx.hir().body(body);
             let body_expr = &body.value;
@@ -33,8 +34,10 @@ pub(super) fn check<'tcx>(
             if eager_or_lazy::switch_to_eager_eval(cx, body_expr) {
                 let msg = if is_option {
                     "unnecessary closure used to substitute value for `Option::None`"
-                } else {
+                } else if is_result {
                     "unnecessary closure used to substitute value for `Result::Err`"
+                } else {
+                    "unnecessary closure used with `bool::then`"
                 };
                 let applicability = if body
                     .params
index be7df08d89f0521de6f2bd871d415ded2e639c2d..8224e80c9ccb36cb5f75ef2e025ddc7b9074e998 100644 (file)
@@ -301,7 +301,7 @@ fn in_attributes_expansion(expr: &Expr<'_>) -> bool {
     use rustc_span::hygiene::MacroKind;
     if expr.span.from_expansion() {
         let data = expr.span.ctxt().outer_expn_data();
-        matches!(data.kind, ExpnKind::Macro(MacroKind::Attr|MacroKind::Derive, _))
+        matches!(data.kind, ExpnKind::Macro(MacroKind::Attr | MacroKind::Derive, _))
     } else {
         false
     }
index d466d54a6ba5db92c5af6f2f210644cbb73c9b55..254d9a70010a8e0ab10556f5b8ef322e46299205 100644 (file)
@@ -9,7 +9,7 @@
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for type parameters which are positioned inconsistently between
-    /// a type definition and impl block. Specifically, a paramater in an impl
+    /// a type definition and impl block. Specifically, a parameter in an impl
     /// block which has the same name as a parameter in the type def, but is in
     /// a different place.
     ///
index 093ec389335db80d9a75e6ca6b566aad3c02fc16..5c45ee6d94ad3c58ad33daeffc50ae747e5999de 100644 (file)
@@ -88,15 +88,9 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
                             // shouldn't be implemented when it is hidden in docs
                             return;
                         }
-                        if impl_item
-                            .generics
-                            .params
-                            .iter()
-                            .any(|gen| matches!(gen.kind, hir::GenericParamKind::Type { .. }))
-                        {
-                            // when the result of `new()` depends on a type parameter we should not require
-                            // an
-                            // impl of `Default`
+                        if !impl_item.generics.params.is_empty() {
+                            // when the result of `new()` depends on a parameter we should not require
+                            // an impl of `Default`
                             return;
                         }
                         if_chain! {
index 3ab4b6c4f6fab72c079a5b5d6ff28d112e164c2e..6ad6837f0e3507afdb92fdcc0e2fcfbd3d6fe30d 100644 (file)
@@ -31,7 +31,7 @@
     /// and friends since the string is already preprocessed when Clippy lints
     /// can see it.
     ///
-    /// # Example
+    /// ### Example
     /// ```rust
     /// let one = "\033[1m Bold? \033[0m";  // \033 intended as escape
     /// let two = "\033\0";                 // \033 intended as null-3-3
index d461668077e0a51f3f2c39b6db51b4bd7913d3d5..413a740be25a529c0fe8fb64361dd38d567de634 100644 (file)
@@ -11,8 +11,8 @@
 use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
 use rustc_hir::intravisit::{walk_expr, walk_stmt, FnKind, Visitor};
 use rustc_hir::{
-    Arm, Closure, Block, Body, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path, PathSegment,
-    QPath, Stmt, StmtKind, TyKind, UnOp,
+    Arm, Block, Body, Closure, Expr, ExprKind, Guard, HirId, ImplicitSelfKind, Let, Local, Pat, PatKind, Path,
+    PathSegment, QPath, Stmt, StmtKind, TyKind, UnOp,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
index ea5a8f0858b66aa14d084b55a585fa8ae7950be0..44f153cffac511401f43c871690590fd3d29da43 100644 (file)
@@ -146,7 +146,7 @@ fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) ->
                 });
                 if let ExprKind::Path(QPath::Resolved(None, Path { res: Res::Local(local_id), .. })) = e.kind {
                     match some_captures.get(local_id)
-                        .or_else(|| (method_sugg == "map_or_else").then(|| ()).and_then(|_| none_captures.get(local_id)))
+                        .or_else(|| (method_sugg == "map_or_else").then_some(()).and_then(|_| none_captures.get(local_id)))
                     {
                         Some(CaptureKind::Value | CaptureKind::Ref(Mutability::Mut)) => return None,
                         Some(CaptureKind::Ref(Mutability::Not)) if as_mut => return None,
index 8571607054a0070bed5709fe483b66b8008b3394..8534d8a29f10df201bd146ded37be1afdb409dbe 100644 (file)
@@ -502,7 +502,7 @@ fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Optio
             .iter()
             .filter_map(get_rptr_lm)
             .filter(|&(lt, _, _)| cx.tcx.named_region(lt.hir_id) == out_region)
-            .map(|(_, mutability, span)| (mutability == Mutability::Not).then(|| span))
+            .map(|(_, mutability, span)| (mutability == Mutability::Not).then_some(span))
             .collect();
         if let Some(args) = args
             && !args.is_empty()
index 899568a933f7afe196b890a5f81e146aca01ec2a..f0155ed6051f6b7c11408130d1b52d14e167076b 100644 (file)
@@ -1,16 +1,19 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{eq_expr_value, is_lang_ctor, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt};
+use clippy_utils::{
+    eq_expr_value, get_parent_node, is_else_clause, is_lang_ctor, path_to_local, path_to_local_id, peel_blocks,
+    peel_blocks_with_stmt,
+};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::LangItem::{OptionNone, OptionSome, ResultOk};
-use rustc_hir::{BindingAnnotation, Expr, ExprKind, PatKind};
+use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
+use rustc_hir::{BindingAnnotation, Expr, ExprKind, Node, PatKind, PathSegment, QPath};
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::Ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
+use rustc_span::{sym, symbol::Symbol};
 
 declare_clippy_lint! {
     /// ### What it does
 
 declare_lint_pass!(QuestionMark => [QUESTION_MARK]);
 
-impl QuestionMark {
-    /// Checks if the given expression on the given context matches the following structure:
+enum IfBlockType<'hir> {
+    /// An `if x.is_xxx() { a } else { b } ` expression.
     ///
-    /// ```ignore
-    /// if option.is_none() {
-    ///    return None;
-    /// }
-    /// ```
-    ///
-    /// ```ignore
-    /// if result.is_err() {
-    ///     return result;
-    /// }
-    /// ```
+    /// Contains: caller (x), caller_type, call_sym (is_xxx), if_then (a), if_else (b)
+    IfIs(
+        &'hir Expr<'hir>,
+        Ty<'hir>,
+        Symbol,
+        &'hir Expr<'hir>,
+        Option<&'hir Expr<'hir>>,
+    ),
+    /// An `if let Xxx(a) = b { c } else { d }` expression.
     ///
-    /// If it matches, it will suggest to use the question mark operator instead
-    fn check_is_none_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if_chain! {
-            if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
-            if let ExprKind::MethodCall(segment, args, _) = &cond.kind;
-            if let Some(subject) = args.get(0);
-            if (Self::option_check_and_early_return(cx, subject, then) && segment.ident.name == sym!(is_none)) ||
-                (Self::result_check_and_early_return(cx, subject, then) && segment.ident.name == sym!(is_err));
-            then {
-                let mut applicability = Applicability::MachineApplicable;
-                let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability);
-                let mut replacement: Option<String> = None;
-                if let Some(else_inner) = r#else {
-                    if eq_expr_value(cx, subject, peel_blocks(else_inner)) {
-                        replacement = Some(format!("Some({}?)", receiver_str));
-                    }
-                } else if Self::moves_by_default(cx, subject)
-                    && !matches!(subject.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
-                {
-                    replacement = Some(format!("{}.as_ref()?;", receiver_str));
-                } else {
-                    replacement = Some(format!("{}?;", receiver_str));
-                }
+    /// Contains: let_pat_qpath (Xxx), let_pat_type, let_pat_sym (a), let_expr (b), if_then (c),
+    /// if_else (d)
+    IfLet(
+        &'hir QPath<'hir>,
+        Ty<'hir>,
+        Symbol,
+        &'hir Expr<'hir>,
+        &'hir Expr<'hir>,
+        Option<&'hir Expr<'hir>>,
+    ),
+}
 
-                if let Some(replacement_str) = replacement {
-                    span_lint_and_sugg(
-                        cx,
-                        QUESTION_MARK,
-                        expr.span,
-                        "this block may be rewritten with the `?` operator",
-                        "replace it with",
-                        replacement_str,
-                        applicability,
-                    );
+/// Checks if the given expression on the given context matches the following structure:
+///
+/// ```ignore
+/// if option.is_none() {
+///    return None;
+/// }
+/// ```
+///
+/// ```ignore
+/// if result.is_err() {
+///     return result;
+/// }
+/// ```
+///
+/// If it matches, it will suggest to use the question mark operator instead
+fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
+    if_chain! {
+        if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
+        if !is_else_clause(cx.tcx, expr);
+        if let ExprKind::MethodCall(segment, args, _) = &cond.kind;
+        if let Some(caller) = args.get(0);
+        let caller_ty = cx.typeck_results().expr_ty(caller);
+        let if_block = IfBlockType::IfIs(caller, caller_ty, segment.ident.name, then, r#else);
+        if is_early_return(sym::Option, cx, &if_block) || is_early_return(sym::Result, cx, &if_block);
+        then {
+            let mut applicability = Applicability::MachineApplicable;
+            let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability);
+            let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx.at(caller.span), cx.param_env) &&
+                !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
+            let sugg = if let Some(else_inner) = r#else {
+                if eq_expr_value(cx, caller, peel_blocks(else_inner)) {
+                    format!("Some({}?)", receiver_str)
+                } else {
+                    return;
                 }
-            }
+            } else {
+                format!("{}{}?;", receiver_str, if by_ref { ".as_ref()" } else { "" })
+            };
+
+            span_lint_and_sugg(
+                cx,
+                QUESTION_MARK,
+                expr.span,
+                "this block may be rewritten with the `?` operator",
+                "replace it with",
+                sugg,
+                applicability,
+            );
         }
     }
+}
 
-    fn check_if_let_some_or_err_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if_chain! {
-            if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) })
-                = higher::IfLet::hir(cx, expr);
-            if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind;
-            if (Self::option_check_and_early_return(cx, let_expr, if_else) && is_lang_ctor(cx, path1, OptionSome)) ||
-                (Self::result_check_and_early_return(cx, let_expr, if_else) && is_lang_ctor(cx, path1, ResultOk));
-
-            if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind;
+fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
+    if_chain! {
+        if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else }) = higher::IfLet::hir(cx, expr);
+        if !is_else_clause(cx.tcx, expr);
+        if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind;
+        if let PatKind::Binding(annot, bind_id, ident, _) = fields[0].kind;
+        let caller_ty = cx.typeck_results().expr_ty(let_expr);
+        let if_block = IfBlockType::IfLet(path1, caller_ty, ident.name, let_expr, if_then, if_else);
+        if (is_early_return(sym::Option, cx, &if_block) && path_to_local_id(peel_blocks(if_then), bind_id))
+            || is_early_return(sym::Result, cx, &if_block);
+        if if_else.map(|e| eq_expr_value(cx, let_expr, peel_blocks(e))).filter(|e| *e).is_none();
+        then {
+            let mut applicability = Applicability::MachineApplicable;
+            let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
             let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
-            if path_to_local_id(peel_blocks(if_then), bind_id);
-            then {
-                let mut applicability = Applicability::MachineApplicable;
-                let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
-                let replacement = format!("{}{}?", receiver_str, if by_ref { ".as_ref()" } else { "" },);
-
-                span_lint_and_sugg(
-                    cx,
-                    QUESTION_MARK,
-                    expr.span,
-                    "this if-let-else may be rewritten with the `?` operator",
-                    "replace it with",
-                    replacement,
-                    applicability,
-                );
-            }
+            let requires_semi = matches!(get_parent_node(cx.tcx, expr.hir_id), Some(Node::Stmt(_)));
+            let sugg = format!(
+                "{}{}?{}",
+                receiver_str,
+                if by_ref { ".as_ref()" } else { "" },
+                if requires_semi { ";" } else { "" }
+            );
+            span_lint_and_sugg(
+                cx,
+                QUESTION_MARK,
+                expr.span,
+                "this block may be rewritten with the `?` operator",
+                "replace it with",
+                sugg,
+                applicability,
+            );
         }
     }
+}
 
-    fn result_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool {
-        Self::is_result(cx, expr) && Self::expression_returns_unmodified_err(nested_expr, expr)
-    }
-
-    fn option_check_and_early_return(cx: &LateContext<'_>, expr: &Expr<'_>, nested_expr: &Expr<'_>) -> bool {
-        Self::is_option(cx, expr) && Self::expression_returns_none(cx, nested_expr)
-    }
-
-    fn moves_by_default(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
-        let expr_ty = cx.typeck_results().expr_ty(expression);
-
-        !expr_ty.is_copy_modulo_regions(cx.tcx.at(expression.span), cx.param_env)
-    }
-
-    fn is_option(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
-        let expr_ty = cx.typeck_results().expr_ty(expression);
-
-        is_type_diagnostic_item(cx, expr_ty, sym::Option)
-    }
-
-    fn is_result(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
-        let expr_ty = cx.typeck_results().expr_ty(expression);
-
-        is_type_diagnostic_item(cx, expr_ty, sym::Result)
-    }
-
-    fn expression_returns_none(cx: &LateContext<'_>, expression: &Expr<'_>) -> bool {
-        match peel_blocks_with_stmt(expression).kind {
-            ExprKind::Ret(Some(expr)) => Self::expression_returns_none(cx, expr),
-            ExprKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
-            _ => false,
-        }
+fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_>) -> bool {
+    match *if_block {
+        IfBlockType::IfIs(caller, caller_ty, call_sym, if_then, _) => {
+            // If the block could be identified as `if x.is_none()/is_err()`,
+            // we then only need to check the if_then return to see if it is none/err.
+            is_type_diagnostic_item(cx, caller_ty, smbl)
+                && expr_return_none_or_err(smbl, cx, if_then, caller, None)
+                && match smbl {
+                    sym::Option => call_sym == sym!(is_none),
+                    sym::Result => call_sym == sym!(is_err),
+                    _ => false,
+                }
+        },
+        IfBlockType::IfLet(qpath, let_expr_ty, let_pat_sym, let_expr, if_then, if_else) => {
+            is_type_diagnostic_item(cx, let_expr_ty, smbl)
+                && match smbl {
+                    sym::Option => {
+                        // We only need to check `if let Some(x) = option` not `if let None = option`,
+                        // because the later one will be suggested as `if option.is_none()` thus causing conflict.
+                        is_lang_ctor(cx, qpath, OptionSome)
+                            && if_else.is_some()
+                            && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, None)
+                    },
+                    sym::Result => {
+                        (is_lang_ctor(cx, qpath, ResultOk)
+                            && if_else.is_some()
+                            && expr_return_none_or_err(smbl, cx, if_else.unwrap(), let_expr, Some(let_pat_sym)))
+                            || is_lang_ctor(cx, qpath, ResultErr)
+                                && expr_return_none_or_err(smbl, cx, if_then, let_expr, Some(let_pat_sym))
+                    },
+                    _ => false,
+                }
+        },
     }
+}
 
-    fn expression_returns_unmodified_err(expr: &Expr<'_>, cond_expr: &Expr<'_>) -> bool {
-        match peel_blocks_with_stmt(expr).kind {
-            ExprKind::Ret(Some(ret_expr)) => Self::expression_returns_unmodified_err(ret_expr, cond_expr),
-            ExprKind::Path(_) => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr),
+fn expr_return_none_or_err(
+    smbl: Symbol,
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    cond_expr: &Expr<'_>,
+    err_sym: Option<Symbol>,
+) -> bool {
+    match peel_blocks_with_stmt(expr).kind {
+        ExprKind::Ret(Some(ret_expr)) => expr_return_none_or_err(smbl, cx, ret_expr, cond_expr, err_sym),
+        ExprKind::Path(ref qpath) => match smbl {
+            sym::Option => is_lang_ctor(cx, qpath, OptionNone),
+            sym::Result => path_to_local(expr).is_some() && path_to_local(expr) == path_to_local(cond_expr),
             _ => false,
-        }
+        },
+        ExprKind::Call(call_expr, args_expr) => {
+            if_chain! {
+                if smbl == sym::Result;
+                if let ExprKind::Path(QPath::Resolved(_, path)) = &call_expr.kind;
+                if let Some(segment) = path.segments.first();
+                if let Some(err_sym) = err_sym;
+                if let Some(arg) = args_expr.first();
+                if let ExprKind::Path(QPath::Resolved(_, arg_path)) = &arg.kind;
+                if let Some(PathSegment { ident, .. }) = arg_path.segments.first();
+                then {
+                    return segment.ident.name == sym::Err && err_sym == ident.name;
+                }
+            }
+            false
+        },
+        _ => false,
     }
 }
 
 impl<'tcx> LateLintPass<'tcx> for QuestionMark {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        Self::check_is_none_or_err_and_early_return(cx, expr);
-        Self::check_if_let_some_or_err_and_early_return(cx, expr);
+        check_is_none_or_err_and_early_return(cx, expr);
+        check_if_let_some_or_err_and_early_return(cx, expr);
     }
 }
index 67129299e2f9294292c1abd84b95a86d74bce721..f9a9b0691935ad82cc3b817f5d276524a18efd41 100644 (file)
@@ -19,7 +19,7 @@
     ///
     /// ### Example
     /// ```ignore
-    /// Regex::new("|")
+    /// Regex::new("(")
     /// ```
     #[clippy::version = "pre 1.29.0"]
     pub INVALID_REGEX,
index bf318c055dad1d48ae38e37b2ceb70fad1a75b74..5dcdab5b8ab90e9a35a57ca5735ff56825ed9aad 100644 (file)
@@ -99,7 +99,7 @@
 
 #[derive(Default)]
 pub(crate) struct Shadow {
-    bindings: Vec<FxHashMap<Symbol, Vec<ItemLocalId>>>,
+    bindings: Vec<(FxHashMap<Symbol, Vec<ItemLocalId>>, LocalDefId)>,
 }
 
 impl_lint_pass!(Shadow => [SHADOW_SAME, SHADOW_REUSE, SHADOW_UNRELATED]);
@@ -121,7 +121,7 @@ fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
 
         let HirId { owner, local_id } = id;
         // get (or insert) the list of items for this owner and symbol
-        let data = self.bindings.last_mut().unwrap();
+        let (ref mut data, scope_owner) = *self.bindings.last_mut().unwrap();
         let items_with_name = data.entry(ident.name).or_default();
 
         // check other bindings with the same name, most recently seen first
@@ -131,7 +131,7 @@ fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
                 return;
             }
 
-            if is_shadow(cx, owner, prev, local_id) {
+            if is_shadow(cx, scope_owner, prev, local_id) {
                 let prev_hir_id = HirId { owner, local_id: prev };
                 lint_shadow(cx, pat, prev_hir_id, ident.span);
                 // only lint against the "nearest" shadowed binding
@@ -144,11 +144,9 @@ fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
 
     fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
         let hir = cx.tcx.hir();
-        if !matches!(
-            hir.body_owner_kind(hir.body_owner_def_id(body.id())),
-            BodyOwnerKind::Closure
-        ) {
-            self.bindings.push(FxHashMap::default());
+        let owner_id = hir.body_owner_def_id(body.id());
+        if !matches!(hir.body_owner_kind(owner_id), BodyOwnerKind::Closure) {
+            self.bindings.push((FxHashMap::default(), owner_id));
         }
     }
 
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
new file mode 100644 (file)
index 0000000..56f2a7b
--- /dev/null
@@ -0,0 +1,134 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_hir::{def::Res, HirId, Path, PathSegment};
+use rustc_lint::{LateContext, LateLintPass, Lint};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{sym, symbol::kw, Symbol};
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Finds items imported through `std` when available through `core`.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// Crates which have `no_std` compatibility may wish to ensure types are imported from core to ensure
+    /// disabling `std` does not cause the crate to fail to compile. This lint is also useful for crates
+    /// migrating to become `no_std` compatible.
+    ///
+    /// ### Example
+    /// ```rust
+    /// use std::hash::Hasher;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use core::hash::Hasher;
+    /// ```
+    #[clippy::version = "1.64.0"]
+    pub STD_INSTEAD_OF_CORE,
+    restriction,
+    "type is imported from std when available in core"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Finds items imported through `std` when available through `alloc`.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// Crates which have `no_std` compatibility and require alloc may wish to ensure types are imported from
+    /// alloc to ensure disabling `std` does not cause the crate to fail to compile. This lint is also useful
+    /// for crates migrating to become `no_std` compatible.
+    ///
+    /// ### Example
+    /// ```rust
+    /// use std::vec::Vec;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # extern crate alloc;
+    /// use alloc::vec::Vec;
+    /// ```
+    #[clippy::version = "1.64.0"]
+    pub STD_INSTEAD_OF_ALLOC,
+    restriction,
+    "type is imported from std when available in alloc"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Finds items imported through `alloc` when available through `core`.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// Crates which have `no_std` compatibility and may optionally require alloc may wish to ensure types are
+    /// imported from alloc to ensure disabling `alloc` does not cause the crate to fail to compile. This lint
+    /// is also useful for crates migrating to become `no_std` compatible.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # extern crate alloc;
+    /// use alloc::slice::from_ref;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use core::slice::from_ref;
+    /// ```
+    #[clippy::version = "1.64.0"]
+    pub ALLOC_INSTEAD_OF_CORE,
+    restriction,
+    "type is imported from alloc when available in core"
+}
+
+declare_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
+
+impl<'tcx> LateLintPass<'tcx> for StdReexports {
+    fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) {
+        // std_instead_of_core
+        check_path(cx, path, sym::std, sym::core, STD_INSTEAD_OF_CORE);
+        // std_instead_of_alloc
+        check_path(cx, path, sym::std, sym::alloc, STD_INSTEAD_OF_ALLOC);
+        // alloc_instead_of_core
+        check_path(cx, path, sym::alloc, sym::core, ALLOC_INSTEAD_OF_CORE);
+    }
+}
+
+fn check_path(cx: &LateContext<'_>, path: &Path<'_>, krate: Symbol, suggested_crate: Symbol, lint: &'static Lint) {
+    if_chain! {
+        // check if path resolves to the suggested crate.
+        if let Res::Def(_, def_id) = path.res;
+        if suggested_crate == cx.tcx.crate_name(def_id.krate);
+
+        // check if the first segment of the path is the crate we want to identify
+        if let Some(path_root_segment) = get_first_segment(path);
+
+        // check if the path matches the crate we want to suggest the other path for.
+        if krate == path_root_segment.ident.name;
+        then {
+            span_lint_and_help(
+                cx,
+                lint,
+                path.span,
+                &format!("used import from `{}` instead of `{}`", krate, suggested_crate),
+                None,
+                &format!("consider importing the item from `{}`", suggested_crate),
+            );
+        }
+    }
+}
+
+/// Returns the first named segment of a [`Path`].
+///
+/// If this is a global path (such as `::std::fmt::Debug`), then the segment after [`kw::PathRoot`]
+/// is returned.
+fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
+    let segment = path.segments.first()?;
+
+    // A global path will have PathRoot as the first segment. In this case, return the segment after.
+    if segment.ident.name == kw::PathRoot {
+        path.segments.get(1)
+    } else {
+        Some(segment)
+    }
+}
index 75d3b040c968f9b3f47889d9a164203e4357a1b6..3cbbda80f3a9fc6d11de47e6baea4481aea61c36 100644 (file)
@@ -73,7 +73,7 @@ fn is_ptr_to_ref(cx: &LateContext<'_>, e: &Expr<'_>, ctxt: SyntaxContext) -> (bo
         && let ExprKind::Unary(UnOp::Deref, derefed_expr) = borrowed_expr.kind
         && cx.typeck_results().expr_ty(derefed_expr).is_unsafe_ptr()
     {
-        (true, (borrowed_expr.span.ctxt() == ctxt || derefed_expr.span.ctxt() == ctxt).then(|| derefed_expr.span))
+        (true, (borrowed_expr.span.ctxt() == ctxt || derefed_expr.span.ctxt() == ctxt).then_some(derefed_expr.span))
     } else {
         (false, None)
     }
index ac63d1823371abdc03adb81822598b10a42086b5..0a42a31fb8cf9e0a96dac08ede5a215e3e736880 100644 (file)
@@ -1,20 +1,20 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 use clippy_utils::{SpanlessEq, SpanlessHash};
 use core::hash::{Hash, Hasher};
 use if_chain::if_chain;
+use itertools::Itertools;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::unhash::UnhashMap;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{
-    GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, PredicateOrigin, QPath, TraitItem, Ty, TyKind,
-    WherePredicate,
+    GenericArg, GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, PredicateOrigin, QPath,
+    TraitBoundModifier, TraitItem, TraitRef, Ty, TyKind, WherePredicate,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::Span;
-use std::fmt::Write as _;
+use rustc_span::{BytePos, Span};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -35,8 +35,8 @@
     /// ```
     #[clippy::version = "1.38.0"]
     pub TYPE_REPETITION_IN_BOUNDS,
-    pedantic,
-    "Types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
+    nursery,
+    "types are repeated unnecessary in trait bounds use `+` instead of using `T: _, T: _`"
 }
 
 declare_clippy_lint! {
     ///
     /// fn func<T>(arg: T) where T: Clone + Default {}
     /// ```
+    ///
+    /// ```rust
+    /// fn foo<T: Default + Default>(bar: T) {}
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn foo<T: Default>(bar: T) {}
+    /// ```
+    ///
+    /// ```rust
+    /// fn foo<T>(bar: T) where T: Default + Default {}
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn foo<T>(bar: T) where T: Default {}
+    /// ```
     #[clippy::version = "1.47.0"]
     pub TRAIT_DUPLICATION_IN_BOUNDS,
-    pedantic,
-    "Check if the same trait bounds are specified twice during a function declaration"
+    nursery,
+    "check if the same trait bounds are specified more than once during a generic declaration"
 }
 
 #[derive(Copy, Clone)]
@@ -87,6 +103,19 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
     fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
         self.check_type_repetition(cx, gen);
         check_trait_bound_duplication(cx, gen);
+        check_bounds_or_where_duplication(cx, gen);
+    }
+
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+        // special handling for self trait bounds as these are not considered generics
+        // ie. trait Foo: Display {}
+        if let Item {
+            kind: ItemKind::Trait(_, _, _, bounds, ..),
+            ..
+        } = item
+        {
+            rollup_traits(cx, bounds, "these bounds contain repeated elements");
+        }
     }
 
     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) {
@@ -178,30 +207,18 @@ impl Eq for SpanlessTy<'_, '_> {}
                 );
 
                 then {
-                    let mut hint_string = format!(
-                        "consider combining the bounds: `{}:",
-                        snippet(cx, p.bounded_ty.span, "_")
+                    let trait_bounds = v
+                        .iter()
+                        .copied()
+                        .chain(p.bounds.iter())
+                        .filter_map(get_trait_info_from_bound)
+                        .map(|(_, _, span)| snippet_with_applicability(cx, span, "..", &mut applicability))
+                        .join(" + ");
+                    let hint_string = format!(
+                        "consider combining the bounds: `{}: {}`",
+                        snippet(cx, p.bounded_ty.span, "_"),
+                        trait_bounds,
                     );
-                    for b in v.iter() {
-                        if let GenericBound::Trait(ref poly_trait_ref, _) = b {
-                            let path = &poly_trait_ref.trait_ref.path;
-                            let _ = write!(hint_string,
-                                " {} +",
-                                snippet_with_applicability(cx, path.span, "..", &mut applicability)
-                            );
-                        }
-                    }
-                    for b in p.bounds.iter() {
-                        if let GenericBound::Trait(ref poly_trait_ref, _) = b {
-                            let path = &poly_trait_ref.trait_ref.path;
-                            let _ = write!(hint_string,
-                                " {} +",
-                                snippet_with_applicability(cx, path.span, "..", &mut applicability)
-                            );
-                        }
-                    }
-                    hint_string.truncate(hint_string.len() - 2);
-                    hint_string.push('`');
                     span_lint_and_help(
                         cx,
                         TYPE_REPETITION_IN_BOUNDS,
@@ -253,10 +270,107 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
     }
 }
 
+#[derive(PartialEq, Eq, Hash, Debug)]
+struct ComparableTraitRef(Res, Vec<Res>);
+
+fn check_bounds_or_where_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
+    if gen.span.from_expansion() {
+        return;
+    }
+
+    for predicate in gen.predicates {
+        if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate {
+            let msg = if predicate.in_where_clause() {
+                "these where clauses contain repeated elements"
+            } else {
+                "these bounds contain repeated elements"
+            };
+            rollup_traits(cx, bound_predicate.bounds, msg);
+        }
+    }
+}
+
 fn get_trait_info_from_bound<'a>(bound: &'a GenericBound<'_>) -> Option<(Res, &'a [PathSegment<'a>], Span)> {
-    if let GenericBound::Trait(t, _) = bound {
-        Some((t.trait_ref.path.res, t.trait_ref.path.segments, t.span))
+    if let GenericBound::Trait(t, tbm) = bound {
+        let trait_path = t.trait_ref.path;
+        let trait_span = {
+            let path_span = trait_path.span;
+            if let TraitBoundModifier::Maybe = tbm {
+                path_span.with_lo(path_span.lo() - BytePos(1)) // include the `?`
+            } else {
+                path_span
+            }
+        };
+        Some((trait_path.res, trait_path.segments, trait_span))
     } else {
         None
     }
 }
+
+// FIXME: ComparableTraitRef does not support nested bounds needed for associated_type_bounds
+fn into_comparable_trait_ref(trait_ref: &TraitRef<'_>) -> ComparableTraitRef {
+    ComparableTraitRef(
+        trait_ref.path.res,
+        trait_ref
+            .path
+            .segments
+            .iter()
+            .filter_map(|segment| {
+                // get trait bound type arguments
+                Some(segment.args?.args.iter().filter_map(|arg| {
+                    if_chain! {
+                        if let GenericArg::Type(ty) = arg;
+                        if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind;
+                        then { return Some(path.res) }
+                    }
+                    None
+                }))
+            })
+            .flatten()
+            .collect(),
+    )
+}
+
+fn rollup_traits(cx: &LateContext<'_>, bounds: &[GenericBound<'_>], msg: &str) {
+    let mut map = FxHashMap::default();
+    let mut repeated_res = false;
+
+    let only_comparable_trait_refs = |bound: &GenericBound<'_>| {
+        if let GenericBound::Trait(t, _) = bound {
+            Some((into_comparable_trait_ref(&t.trait_ref), t.span))
+        } else {
+            None
+        }
+    };
+
+    for bound in bounds.iter().filter_map(only_comparable_trait_refs) {
+        let (comparable_bound, span_direct) = bound;
+        if map.insert(comparable_bound, span_direct).is_some() {
+            repeated_res = true;
+        }
+    }
+
+    if_chain! {
+        if repeated_res;
+        if let [first_trait, .., last_trait] = bounds;
+        then {
+            let all_trait_span = first_trait.span().to(last_trait.span());
+
+            let mut traits = map.values()
+                .filter_map(|span| snippet_opt(cx, *span))
+                .collect::<Vec<_>>();
+            traits.sort_unstable();
+            let traits = traits.join(" + ");
+
+            span_lint_and_sugg(
+                cx,
+                TRAIT_DUPLICATION_IN_BOUNDS,
+                all_trait_span,
+                msg,
+                "try",
+                traits,
+                Applicability::MachineApplicable
+            );
+        }
+    }
+}
index 21a9558ec076a54e938fadc06f56fb6f9fa5ee34..ba51404d214832976e245a61076d08bb7bfbceb8 100644 (file)
@@ -15,19 +15,17 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
                 sym::String => "",
                 _ => "<..>",
             };
+
+            let box_content = format!("{outer}{generic}", outer = item_type);
             span_lint_and_help(
                 cx,
                 BOX_COLLECTION,
                 hir_ty.span,
                 &format!(
-                    "you seem to be trying to use `Box<{outer}{generic}>`. Consider using just `{outer}{generic}`",
-                    outer=item_type,
-                    generic = generic),
+                    "you seem to be trying to use `Box<{box_content}>`. Consider using just `{box_content}`"),
                 None,
                 &format!(
-                    "`{outer}{generic}` is already on the heap, `Box<{outer}{generic}>` makes an extra allocation",
-                    outer=item_type,
-                    generic = generic)
+                    "`{box_content}` is already on the heap, `Box<{box_content}>` makes an extra allocation")
             );
             true
         } else {
@@ -39,7 +37,18 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
 fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Symbol> {
     let param = qpath_generic_tys(qpath).next()?;
     let id = path_def_id(cx, param)?;
-    cx.tcx
-        .get_diagnostic_name(id)
-        .filter(|&name| matches!(name, sym::HashMap | sym::String | sym::Vec))
+    cx.tcx.get_diagnostic_name(id).filter(|&name| {
+        matches!(
+            name,
+            sym::HashMap
+                | sym::String
+                | sym::Vec
+                | sym::HashSet
+                | sym::VecDeque
+                | sym::LinkedList
+                | sym::BTreeMap
+                | sym::BTreeSet
+                | sym::BinaryHeap
+        )
+    })
 }
index 025dd57e83aa59ccfe8e7da39f19ef6521597c4b..04f16fd2161c51a0cdbed5e35419540052e912c5 100644 (file)
@@ -265,14 +265,28 @@ fn span_from_macro_expansion_has_safety_comment(cx: &LateContext<'_>, span: Span
     }
 }
 
+fn get_body_search_span(cx: &LateContext<'_>) -> Option<Span> {
+    let body = cx.enclosing_body?;
+    let map = cx.tcx.hir();
+    let mut span = map.body(body).value.span;
+    for (_, node) in map.parent_iter(body.hir_id) {
+        match node {
+            Node::Expr(e) => span = e.span,
+            Node::Block(_) | Node::Arm(_) | Node::Stmt(_) | Node::Local(_) => (),
+            _ => break,
+        }
+    }
+    Some(span)
+}
+
 fn span_in_body_has_safety_comment(cx: &LateContext<'_>, span: Span) -> bool {
     let source_map = cx.sess().source_map();
     let ctxt = span.ctxt();
     if ctxt == SyntaxContext::root()
-        && let Some(body) = cx.enclosing_body
+        && let Some(search_span) = get_body_search_span(cx)
     {
         if let Ok(unsafe_line) = source_map.lookup_line(span.lo())
-            && let Some(body_span) = walk_span_to_context(cx.tcx.hir().body(body).value.span, SyntaxContext::root())
+            && let Some(body_span) = walk_span_to_context(search_span, SyntaxContext::root())
             && let Ok(body_line) = source_map.lookup_line(body_span.lo())
             && Lrc::ptr_eq(&unsafe_line.sf, &body_line.sf)
             && let Some(src) = unsafe_line.sf.src.as_deref()
index cf509455aad0aeea766170e78a53aedc94377699..aec028d5c4824b63bfad47af38eca5846a18ab1a 100644 (file)
@@ -1,43 +1,40 @@
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::get_parent_node;
 use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::visitors::for_each_value_source;
+use clippy_utils::visitors::{for_each_local_assignment, for_each_value_source};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{Expr, ExprKind, PatKind, Stmt, StmtKind};
+use rustc_hir::{Expr, ExprKind, HirId, HirIdSet, Local, Node, PatKind, QPath, TyKind};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Ty, TypeVisitable, TypeSuperVisitable, TypeVisitor};
+use rustc_middle::ty;
 
 use super::LET_UNIT_VALUE;
 
-pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
-    if let StmtKind::Local(local) = stmt.kind
-        && let Some(init) = local.init
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
+    if let Some(init) = local.init
         && !local.pat.span.from_expansion()
-        && !in_external_macro(cx.sess(), stmt.span)
+        && !in_external_macro(cx.sess(), local.span)
         && cx.typeck_results().pat_ty(local.pat).is_unit()
     {
-        let needs_inferred = for_each_value_source(init, &mut |e| if needs_inferred_result_ty(cx, e) {
-            ControlFlow::Continue(())
-        } else {
-            ControlFlow::Break(())
-        }).is_continue();
-
-        if needs_inferred {
-            if !matches!(local.pat.kind, PatKind::Wild) {
+        if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer))
+            || matches!(local.pat.kind, PatKind::Tuple([], None)))
+            && expr_needs_inferred_result(cx, init)
+        {
+            if !matches!(local.pat.kind, PatKind::Wild | PatKind::Tuple([], None)) {
                 span_lint_and_then(
                     cx,
                     LET_UNIT_VALUE,
-                    stmt.span,
+                    local.span,
                     "this let-binding has unit value",
                     |diag| {
-                            diag.span_suggestion(
-                                local.pat.span,
-                                "use a wild (`_`) binding",
-                                "_",
-                                Applicability::MaybeIncorrect, // snippet
-                            );
+                        diag.span_suggestion(
+                            local.pat.span,
+                            "use a wild (`_`) binding",
+                            "_",
+                            Applicability::MaybeIncorrect, // snippet
+                        );
                     },
                 );
             }
@@ -45,15 +42,15 @@ pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
             span_lint_and_then(
                 cx,
                 LET_UNIT_VALUE,
-                stmt.span,
+                local.span,
                 "this let-binding has unit value",
                 |diag| {
                     if let Some(expr) = &local.init {
                         let snip = snippet_with_macro_callsite(cx, expr.span, "()");
                         diag.span_suggestion(
-                            stmt.span,
+                            local.span,
                             "omit the `let` binding",
-                            format!("{};", snip),
+                            format!("{snip};"),
                             Applicability::MachineApplicable, // snippet
                         );
                     }
@@ -63,48 +60,106 @@ pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
     }
 }
 
-fn needs_inferred_result_ty(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
-    let id = match e.kind {
+/// Checks sub-expressions which create the value returned by the given expression for whether
+/// return value inference is needed. This checks through locals to see if they also need inference
+/// at this point.
+///
+/// e.g.
+/// ```rust,ignore
+/// let bar = foo();
+/// let x: u32 = if true { baz() } else { bar };
+/// ```
+/// Here the sources of the value assigned to `x` would be `baz()`, and `foo()` via the
+/// initialization of `bar`. If both `foo` and `baz` have a return type which require type
+/// inference then this function would return `true`.
+fn expr_needs_inferred_result<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
+    // The locals used for initialization which have yet to be checked.
+    let mut locals_to_check = Vec::new();
+    // All the locals which have been added to `locals_to_check`. Needed to prevent cycles.
+    let mut seen_locals = HirIdSet::default();
+    if !each_value_source_needs_inference(cx, e, &mut locals_to_check, &mut seen_locals) {
+        return false;
+    }
+    while let Some(id) = locals_to_check.pop() {
+        if let Some(Node::Local(l)) = get_parent_node(cx.tcx, id) {
+            if !l.ty.map_or(true, |ty| matches!(ty.kind, TyKind::Infer)) {
+                return false;
+            }
+            if let Some(e) = l.init {
+                if !each_value_source_needs_inference(cx, e, &mut locals_to_check, &mut seen_locals) {
+                    return false;
+                }
+            } else if for_each_local_assignment(cx, id, |e| {
+                if each_value_source_needs_inference(cx, e, &mut locals_to_check, &mut seen_locals) {
+                    ControlFlow::Continue(())
+                } else {
+                    ControlFlow::Break(())
+                }
+            })
+            .is_break()
+            {
+                return false;
+            }
+        }
+    }
+
+    true
+}
+
+fn each_value_source_needs_inference(
+    cx: &LateContext<'_>,
+    e: &Expr<'_>,
+    locals_to_check: &mut Vec<HirId>,
+    seen_locals: &mut HirIdSet,
+) -> bool {
+    for_each_value_source(e, &mut |e| {
+        if needs_inferred_result_ty(cx, e, locals_to_check, seen_locals) {
+            ControlFlow::Continue(())
+        } else {
+            ControlFlow::Break(())
+        }
+    })
+    .is_continue()
+}
+
+fn needs_inferred_result_ty(
+    cx: &LateContext<'_>,
+    e: &Expr<'_>,
+    locals_to_check: &mut Vec<HirId>,
+    seen_locals: &mut HirIdSet,
+) -> bool {
+    let (id, args) = match e.kind {
         ExprKind::Call(
             Expr {
                 kind: ExprKind::Path(ref path),
                 hir_id,
                 ..
             },
-            _,
+            args,
         ) => match cx.qpath_res(path, *hir_id) {
-            Res::Def(DefKind::AssocFn | DefKind::Fn, id) => id,
+            Res::Def(DefKind::AssocFn | DefKind::Fn, id) => (id, args),
             _ => return false,
         },
-        ExprKind::MethodCall(..) => match cx.typeck_results().type_dependent_def_id(e.hir_id) {
-            Some(id) => id,
+        ExprKind::MethodCall(_, args, _) => match cx.typeck_results().type_dependent_def_id(e.hir_id) {
+            Some(id) => (id, args),
             None => return false,
         },
+        ExprKind::Path(QPath::Resolved(None, path)) => {
+            if let Res::Local(id) = path.res
+                && seen_locals.insert(id)
+            {
+                locals_to_check.push(id);
+            }
+            return true;
+        },
         _ => return false,
     };
     let sig = cx.tcx.fn_sig(id).skip_binder();
     if let ty::Param(output_ty) = *sig.output().kind() {
-        sig.inputs().iter().all(|&ty| !ty_contains_param(ty, output_ty.index))
+        sig.inputs().iter().zip(args).all(|(&ty, arg)| {
+            !ty.is_param(output_ty.index) || each_value_source_needs_inference(cx, arg, locals_to_check, seen_locals)
+        })
     } else {
         false
     }
 }
-
-fn ty_contains_param(ty: Ty<'_>, index: u32) -> bool {
-    struct Visitor(u32);
-    impl<'tcx> TypeVisitor<'tcx> for Visitor {
-        type BreakTy = ();
-        fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
-            if let ty::Param(ty) = *ty.kind() {
-                if ty.index == self.0 {
-                    ControlFlow::BREAK
-                } else {
-                    ControlFlow::CONTINUE
-                }
-            } else {
-                ty.super_visit_with(self)
-            }
-        }
-    }
-    ty.visit_with(&mut Visitor(index)).is_break()
-}
index a9e2073dec251de0d50b62c3b8d12df6531409f3..6aa86a57c9bdf58b2ccb07cd804c5a1d292da1b6 100644 (file)
@@ -3,7 +3,7 @@
 mod unit_cmp;
 mod utils;
 
-use rustc_hir::{Expr, Stmt};
+use rustc_hir::{Expr, Local};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
@@ -98,9 +98,9 @@
 
 declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP, UNIT_ARG]);
 
-impl LateLintPass<'_> for UnitTypes {
-    fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
-        let_unit_value::check(cx, stmt);
+impl<'tcx> LateLintPass<'tcx> for UnitTypes {
+    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
+        let_unit_value::check(cx, local);
     }
 
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
index a94f0357977eab8766bea03e03d1eee36b0b003f..b309653291b11ce58d3d1d3a45eb696705490cbd 100644 (file)
@@ -20,8 +20,8 @@
 use rustc_hir::hir_id::CRATE_HIR_ID;
 use rustc_hir::intravisit::Visitor;
 use rustc_hir::{
-    BinOpKind, Block, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty, TyKind,
-    UnOp,
+    BinOpKind, Block, Closure, Expr, ExprKind, HirId, Item, Local, MutTy, Mutability, Node, Path, Stmt, StmtKind, Ty,
+    TyKind, UnOp,
 };
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::nested_filter;
@@ -730,8 +730,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
             if let ExprKind::Call(func, and_then_args) = expr.kind;
             if is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"]);
             if and_then_args.len() == 5;
-            if let ExprKind::Closure { body, .. } = &and_then_args[4].kind;
-            let body = cx.tcx.hir().body(*body);
+            if let ExprKind::Closure(&Closure { body, .. }) = &and_then_args[4].kind;
+            let body = cx.tcx.hir().body(body);
             let only_expr = peel_blocks_with_stmt(&body.value);
             if let ExprKind::MethodCall(ps, span_call_args, _) = &only_expr.kind;
             if let ExprKind::Path(..) = span_call_args[0].kind;
index 6518e0a6ea07390620fff232344e21c30312b417..92934c16d4b406a252e121355671e60dfc425dbe 100644 (file)
@@ -17,7 +17,7 @@
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{
-    self as hir, def::DefKind, intravisit, intravisit::Visitor, ExprKind, Item, ItemKind, Mutability, QPath,
+    self as hir, def::DefKind, intravisit, intravisit::Visitor, Closure, ExprKind, Item, ItemKind, Mutability, QPath,
 };
 use rustc_lint::{CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
 use rustc_middle::hir::nested_filter;
@@ -843,7 +843,7 @@ fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
 fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
     DEFAULT_LINT_LEVELS
         .iter()
-        .find_map(|(group_name, group_level)| (*group_name == lint_group).then(|| *group_level))
+        .find_map(|(group_name, group_level)| (*group_name == lint_group).then_some(*group_level))
 }
 
 pub(super) fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
@@ -958,7 +958,7 @@ fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hi
 }
 
 fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
-    if let ExprKind::Closure { body, .. } = closure_expr.kind {
+    if let ExprKind::Closure(&Closure { body, .. }) = closure_expr.kind {
         let mut scanner = IsMultiSpanScanner::new(cx);
         intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body));
         return scanner.is_multi_part();
@@ -1018,7 +1018,7 @@ fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
 /// This visitor finds the highest applicability value in the visited expressions
 struct ApplicabilityResolver<'a, 'hir> {
     cx: &'a LateContext<'hir>,
-    /// This is the index of hightest `Applicability` for `paths::APPLICABILITY_VALUES`
+    /// This is the index of highest `Applicability` for `paths::APPLICABILITY_VALUES`
     applicability_index: Option<usize>,
 }
 
index 67b2bc8c3f3cd86e823868a0475f083ea535f659..08b8894752011c86a51f8ea36aa15eba82a19cd8 100644 (file)
@@ -515,7 +515,7 @@ fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str_lit: &StrLit) -> Option<Si
             args.push(arg, span);
         }
 
-        parser.errors.is_empty().then(move || args)
+        parser.errors.is_empty().then_some(args)
     }
 
     /// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
index 431b09d53c33b25a9542f719aa75deb899771692..b226026323be835a7f5be3231f8fe70f491bdc80 100644 (file)
@@ -169,7 +169,12 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
         (Field(lp, lf), Field(rp, rf)) => eq_id(*lf, *rf) && eq_expr(lp, rp),
         (Match(ls, la), Match(rs, ra)) => eq_expr(ls, rs) && over(la, ra, eq_arm),
         (Closure(lb, lc, la, lm, lf, le, _), Closure(rb, rc, ra, rm, rf, re, _)) => {
-            eq_closure_binder(lb, rb) && lc == rc && la.is_async() == ra.is_async() && lm == rm && eq_fn_decl(lf, rf) && eq_expr(le, re)
+            eq_closure_binder(lb, rb)
+                && lc == rc
+                && la.is_async() == ra.is_async()
+                && lm == rm
+                && eq_fn_decl(lf, rf)
+                && eq_expr(le, re)
         },
         (Async(lc, _, lb), Async(rc, _, rb)) => lc == rc && eq_block(lb, rb),
         (Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt),
@@ -564,8 +569,9 @@ pub fn eq_fn_decl(l: &FnDecl, r: &FnDecl) -> bool {
 pub fn eq_closure_binder(l: &ClosureBinder, r: &ClosureBinder) -> bool {
     match (l, r) {
         (ClosureBinder::NotPresent, ClosureBinder::NotPresent) => true,
-        (ClosureBinder::For { generic_params: lp, .. }, ClosureBinder::For { generic_params: rp, .. }) => 
-            lp.len() == rp.len() && std::iter::zip(lp.iter(), rp.iter()).all(|(l, r)| eq_generic_param(l, r)),
+        (ClosureBinder::For { generic_params: lp, .. }, ClosureBinder::For { generic_params: rp, .. }) => {
+            lp.len() == rp.len() && std::iter::zip(lp.iter(), rp.iter()).all(|(l, r)| eq_generic_param(l, r))
+        },
         _ => false,
     }
 }
@@ -610,7 +616,7 @@ pub fn eq_ext(l: &Extern, r: &Extern) -> bool {
     use Extern::*;
     match (l, r) {
         (None, None) | (Implicit(_), Implicit(_)) => true,
-        (Explicit(l,_), Explicit(r,_)) => eq_str_lit(l, r),
+        (Explicit(l, _), Explicit(r, _)) => eq_str_lit(l, r),
         _ => false,
     }
 }
index 1a8e8c996316ff75a9e8df8b74905794b2f1aa9d..77c974582ecb0e9b604f3e8ac432399a215efbe6 100644 (file)
@@ -6,9 +6,9 @@
 use rustc_hir::def::Res;
 use rustc_hir::HirIdMap;
 use rustc_hir::{
-    ArrayLen, BinOpKind, Closure, Block, BodyId, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard, HirId,
-    InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, PathSegment, QPath, Stmt,
-    StmtKind, Ty, TyKind, TypeBinding,
+    ArrayLen, BinOpKind, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, Guard,
+    HirId, InlineAsmOperand, Let, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path, PathSegment, QPath,
+    Stmt, StmtKind, Ty, TyKind, TypeBinding,
 };
 use rustc_lexer::{tokenize, TokenKind};
 use rustc_lint::LateContext;
@@ -102,7 +102,7 @@ pub struct HirEqInterExpr<'a, 'b, 'tcx> {
 impl HirEqInterExpr<'_, '_, '_> {
     pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
         match (&left.kind, &right.kind) {
-            (&StmtKind::Local(l, ), &StmtKind::Local(r, )) => {
+            (&StmtKind::Local(l), &StmtKind::Local(r)) => {
                 // This additional check ensures that the type of the locals are equivalent even if the init
                 // expression or type have some inferred parts.
                 if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
@@ -922,7 +922,7 @@ pub fn hash_stmt(&mut self, b: &Stmt<'_>) {
         std::mem::discriminant(&b.kind).hash(&mut self.s);
 
         match &b.kind {
-            StmtKind::Local(local) => {
+            StmtKind::Local(local) => {
                 self.hash_pat(local.pat);
                 if let Some(init) = local.init {
                     self.hash_expr(init);
index 0089e1b64006fa3d4967484faf3f284f86efb429..2fdda9fac1629703edbca57d3327843de120d863 100644 (file)
@@ -93,7 +93,9 @@
     ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType,
     PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType,
 };
-use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture};
+use rustc_middle::ty::{
+    layout::IntegerExt, BorrowKind, ClosureKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeVisitable, UpvarCapture,
+};
 use rustc_middle::ty::{FloatTy, IntTy, UintTy};
 use rustc_semver::RustcVersion;
 use rustc_session::Session;
 use rustc_target::abi::Integer;
 
 use crate::consts::{constant, Constant};
-use crate::ty::{can_partially_move_ty, is_copy, is_recursively_primitive_type};
+use crate::ty::{can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type, ty_is_fn_once_param};
 use crate::visitors::expr_visitor_no_bodies;
 
 pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
@@ -890,7 +892,7 @@ fn pat_capture_kind(cx: &LateContext<'_>, pat: &Pat<'_>) -> CaptureKind {
             Node::Expr(e) => match e.kind {
                 ExprKind::AddrOf(_, mutability, _) => return CaptureKind::Ref(mutability),
                 ExprKind::Index(..) | ExprKind::Unary(UnOp::Deref, _) => capture = CaptureKind::Ref(Mutability::Not),
-                ExprKind::Assign(lhs, ..) | ExprKind::Assign(_, lhs, _) if lhs.hir_id == child_id => {
+                ExprKind::Assign(lhs, ..) | ExprKind::AssignOp(_, lhs, _) if lhs.hir_id == child_id => {
                     return CaptureKind::Ref(Mutability::Mut);
                 },
                 ExprKind::Field(..) => {
@@ -1016,7 +1018,7 @@ fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) {
         captures: HirIdMap::default(),
     };
     v.visit_expr(expr);
-    v.allow_closure.then(|| v.captures)
+    v.allow_closure.then_some(v.captures)
 }
 
 /// Returns the method names and argument list of nested method call expressions that make up
@@ -1197,16 +1199,54 @@ pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
 }
 
 /// Gets the loop or closure enclosing the given expression, if any.
-pub fn get_enclosing_loop_or_closure<'tcx>(tcx: TyCtxt<'tcx>, expr: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
-    for (_, node) in tcx.hir().parent_iter(expr.hir_id) {
+pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &Expr<'_>,
+) -> Option<&'tcx Expr<'tcx>> {
+    for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
         match node {
-            Node::Expr(
-                e @ Expr {
-                    kind: ExprKind::Loop(..) | ExprKind::Closure { .. },
-                    ..
+            Node::Expr(e) => match e.kind {
+                ExprKind::Closure { .. } => {
+                    if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
+                        && subs.as_closure().kind() == ClosureKind::FnOnce
+                    {
+                        continue;
+                    }
+                    let is_once = walk_to_expr_usage(cx, e, |node, id| {
+                        let Node::Expr(e) = node else {
+                            return None;
+                        };
+                        match e.kind {
+                            ExprKind::Call(f, _) if f.hir_id == id => Some(()),
+                            ExprKind::Call(f, args) => {
+                                let i = args.iter().position(|arg| arg.hir_id == id)?;
+                                let sig = expr_sig(cx, f)?;
+                                let predicates = sig
+                                    .predicates_id()
+                                    .map_or(cx.param_env, |id| cx.tcx.param_env(id))
+                                    .caller_bounds();
+                                sig.input(i).and_then(|ty| {
+                                    ty_is_fn_once_param(cx.tcx, ty.skip_binder(), predicates).then_some(())
+                                })
+                            },
+                            ExprKind::MethodCall(_, args, _) => {
+                                let i = args.iter().position(|arg| arg.hir_id == id)?;
+                                let id = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
+                                let ty = cx.tcx.fn_sig(id).skip_binder().inputs()[i];
+                                ty_is_fn_once_param(cx.tcx, ty, cx.tcx.param_env(id).caller_bounds()).then_some(())
+                            },
+                            _ => None,
+                        }
+                    })
+                    .is_some();
+                    if !is_once {
+                        return Some(e);
+                    }
                 },
-            ) => return Some(e),
-            Node::Expr(_) | Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
+                ExprKind::Loop(..) => return Some(e),
+                _ => (),
+            },
+            Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
             _ => break,
         }
     }
index b09c929f76e2267cbc6145d0e95402346a9e0542..9e238c6f1ac0ef84a8883d8d6c84d695ccd28cfd 100644 (file)
@@ -12,6 +12,7 @@ macro_rules! msrv_aliases {
 
 // names may refer to stabilized feature flags or library items
 msrv_aliases! {
+    1,62,0 { BOOL_THEN_SOME }
     1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN }
     1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
     1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS }
index 6542e77113b459e156eb0072fce4391869881b1b..05429d05d9ebe8285698298b598daeaab5eb1347 100644 (file)
 pub const STR_CHARS: [&str; 4] = ["core", "str", "<impl str>", "chars"];
 pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
 pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
+pub const STR_FROM_UTF8_UNCHECKED: [&str; 4] = ["core", "str", "converts", "from_utf8_unchecked"];
 pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
 pub const STR_STARTS_WITH: [&str; 4] = ["core", "str", "<impl str>", "starts_with"];
 #[cfg(feature = "internal")]
index f88a92fb11c111146851e4213a6508c0ec26b2ac..1197fe914de46853dd3dcdde12daec9a6097fbcc 100644 (file)
@@ -353,7 +353,7 @@ pub fn snippet_with_context<'a>(
 /// span containing `m!(0)`.
 pub fn walk_span_to_context(span: Span, outer: SyntaxContext) -> Option<Span> {
     let outer_span = hygiene::walk_chain(span, outer);
-    (outer_span.ctxt() == outer).then(|| outer_span)
+    (outer_span.ctxt() == outer).then_some(outer_span)
 }
 
 /// Removes block comments from the given `Vec` of lines.
index 4326a103d44b70aad43661e1ec6bfa9f69643b96..bad291dfc2513f6a0318437107dff89432b03e55 100644 (file)
@@ -2,6 +2,7 @@
 #![deny(clippy::missing_docs_in_private_items)]
 
 use crate::source::{snippet, snippet_opt, snippet_with_applicability, snippet_with_macro_callsite};
+use crate::ty::expr_sig;
 use crate::{get_parent_expr_for_hir, higher};
 use rustc_ast::util::parser::AssocOp;
 use rustc_ast::{ast, token};
@@ -18,7 +19,6 @@
 use rustc_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 use std::borrow::Cow;
 use std::fmt::{Display, Write as _};
-use std::iter;
 use std::ops::{Add, Neg, Not, Sub};
 
 /// A helper type to build suggestion correctly handling parentheses.
@@ -861,23 +861,37 @@ pub fn finish(&mut self) -> String {
 
     /// indicates whether the function from `parent_expr` takes its args by double reference
     fn func_takes_arg_by_double_ref(&self, parent_expr: &'tcx hir::Expr<'_>, cmt_hir_id: HirId) -> bool {
-        let (call_args, inputs) = match parent_expr.kind {
+        let ty = match parent_expr.kind {
             ExprKind::MethodCall(_, call_args, _) => {
-                if let Some(method_did) = self.cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) {
-                    (call_args, self.cx.tcx.fn_sig(method_did).skip_binder().inputs())
+                if let Some(sig) = self
+                    .cx
+                    .typeck_results()
+                    .type_dependent_def_id(parent_expr.hir_id)
+                    .map(|did| self.cx.tcx.fn_sig(did).skip_binder())
+                {
+                    call_args
+                        .iter()
+                        .position(|arg| arg.hir_id == cmt_hir_id)
+                        .map(|i| sig.inputs()[i])
                 } else {
                     return false;
                 }
             },
             ExprKind::Call(func, call_args) => {
-                let typ = self.cx.typeck_results().expr_ty(func);
-                (call_args, typ.fn_sig(self.cx.tcx).skip_binder().inputs())
+                if let Some(sig) = expr_sig(self.cx, func) {
+                    call_args
+                        .iter()
+                        .position(|arg| arg.hir_id == cmt_hir_id)
+                        .and_then(|i| sig.input(i))
+                        .map(ty::Binder::skip_binder)
+                } else {
+                    return false;
+                }
             },
             _ => return false,
         };
 
-        iter::zip(call_args, inputs)
-            .any(|(arg, ty)| arg.hir_id == cmt_hir_id && matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref()))
+        ty.map_or(false, |ty| matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref()))
     }
 }
 
index a426fa1b0ffcfc927eb90dc075162f04dae1c21c..a05d633d980c3247b18f9e94140d379a80dd9c9c 100644 (file)
@@ -147,7 +147,7 @@ pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<
 /// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`].
 /// * [Common tools for writing lints] for an example how to use this function and other options.
 ///
-/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/doc/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
+/// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
 pub fn implements_trait<'tcx>(
     cx: &LateContext<'tcx>,
     ty: Ty<'tcx>,
@@ -501,7 +501,7 @@ pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(P
 /// A signature for a function like type.
 #[derive(Clone, Copy)]
 pub enum ExprFnSig<'tcx> {
-    Sig(Binder<'tcx, FnSig<'tcx>>),
+    Sig(Binder<'tcx, FnSig<'tcx>>, Option<DefId>),
     Closure(Option<&'tcx FnDecl<'tcx>>, Binder<'tcx, FnSig<'tcx>>),
     Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>),
 }
@@ -510,7 +510,7 @@ impl<'tcx> ExprFnSig<'tcx> {
     /// bounds only for variadic functions, otherwise this will panic.
     pub fn input(self, i: usize) -> Option<Binder<'tcx, Ty<'tcx>>> {
         match self {
-            Self::Sig(sig) => {
+            Self::Sig(sig, _) => {
                 if sig.c_variadic() {
                     sig.inputs().map_bound(|inputs| inputs.get(i).copied()).transpose()
                 } else {
@@ -527,7 +527,7 @@ pub fn input(self, i: usize) -> Option<Binder<'tcx, Ty<'tcx>>> {
     /// functions, otherwise this will panic.
     pub fn input_with_hir(self, i: usize) -> Option<(Option<&'tcx hir::Ty<'tcx>>, Binder<'tcx, Ty<'tcx>>)> {
         match self {
-            Self::Sig(sig) => {
+            Self::Sig(sig, _) => {
                 if sig.c_variadic() {
                     sig.inputs()
                         .map_bound(|inputs| inputs.get(i).copied())
@@ -549,22 +549,29 @@ pub fn input_with_hir(self, i: usize) -> Option<(Option<&'tcx hir::Ty<'tcx>>, Bi
     /// specified.
     pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> {
         match self {
-            Self::Sig(sig) | Self::Closure(_, sig) => Some(sig.output()),
+            Self::Sig(sig, _) | Self::Closure(_, sig) => Some(sig.output()),
             Self::Trait(_, output) => output,
         }
     }
+
+    pub fn predicates_id(&self) -> Option<DefId> {
+        if let ExprFnSig::Sig(_, id) = *self { id } else { None }
+    }
 }
 
 /// If the expression is function like, get the signature for it.
 pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
     if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
-        Some(ExprFnSig::Sig(cx.tcx.fn_sig(id)))
+        Some(ExprFnSig::Sig(cx.tcx.fn_sig(id), Some(id)))
     } else {
         ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs())
     }
 }
 
 fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
+    if ty.is_box() {
+        return ty_sig(cx, ty.boxed_ty());
+    }
     match *ty.kind() {
         ty::Closure(id, subs) => {
             let decl = id
@@ -572,8 +579,9 @@ fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>>
                 .and_then(|id| cx.tcx.hir().fn_decl_by_hir_id(cx.tcx.hir().local_def_id_to_hir_id(id)));
             Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
         },
-        ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs))),
-        ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)),
+        ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs), Some(id))),
+        ty::Opaque(id, _) => ty_sig(cx, cx.tcx.type_of(id)),
+        ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)),
         ty::Dynamic(bounds, _) => {
             let lang_items = cx.tcx.lang_items();
             match bounds.principal() {
@@ -789,3 +797,33 @@ pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx Va
         _ => None,
     }
 }
+
+/// Checks if the type is a type parameter implementing `FnOnce`, but not `FnMut`.
+pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [Predicate<'_>]) -> bool {
+    let ty::Param(ty) = *ty.kind() else {
+        return false;
+    };
+    let lang = tcx.lang_items();
+    let (Some(fn_once_id), Some(fn_mut_id), Some(fn_id))
+        = (lang.fn_once_trait(), lang.fn_mut_trait(), lang.fn_trait())
+    else {
+        return false;
+    };
+    predicates
+        .iter()
+        .try_fold(false, |found, p| {
+            if let PredicateKind::Trait(p) = p.kind().skip_binder()
+            && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
+            && ty.index == self_ty.index
+        {
+            // This should use `super_traits_of`, but that's a private function.
+            if p.trait_ref.def_id == fn_once_id {
+                return Some(true);
+            } else if p.trait_ref.def_id == fn_mut_id || p.trait_ref.def_id == fn_id {
+                return None;
+            }
+        }
+            Some(found)
+        })
+        .unwrap_or(false)
+}
index 68cfa8c1aa8ec22e5a96a53e473759943cd018fa..bae8ad9f5659067030d39315aa3276d68a4910a5 100644 (file)
@@ -5,7 +5,7 @@
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
 use rustc_hir::{
-    Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, QPath, Stmt, UnOp,
+    Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, Pat, QPath, Stmt, UnOp,
     UnsafeSource, Unsafety,
 };
 use rustc_lint::LateContext;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::{self, Ty, TypeckResults};
+use rustc_span::Span;
+
+mod internal {
+    /// Trait for visitor functions to control whether or not to descend to child nodes. Implemented
+    /// for only two types. `()` always descends. `Descend` allows controlled descent.
+    pub trait Continue {
+        fn descend(&self) -> bool;
+    }
+}
+use internal::Continue;
+
+impl Continue for () {
+    fn descend(&self) -> bool {
+        true
+    }
+}
+
+/// Allows for controlled descent when using visitor functions. Use `()` instead when always
+/// descending into child nodes.
+#[derive(Clone, Copy)]
+pub enum Descend {
+    Yes,
+    No,
+}
+impl From<bool> for Descend {
+    fn from(from: bool) -> Self {
+        if from { Self::Yes } else { Self::No }
+    }
+}
+impl Continue for Descend {
+    fn descend(&self) -> bool {
+        matches!(self, Self::Yes)
+    }
+}
+
+/// Calls the given function once for each expression contained. This does not enter any bodies or
+/// nested items.
+pub fn for_each_expr<'tcx, B, C: Continue>(
+    node: impl Visitable<'tcx>,
+    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
+) -> Option<B> {
+    struct V<B, F> {
+        f: F,
+        res: Option<B>,
+    }
+    impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<B, F> {
+        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
+            if self.res.is_some() {
+                return;
+            }
+            match (self.f)(e) {
+                ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
+                ControlFlow::Break(b) => self.res = Some(b),
+                ControlFlow::Continue(_) => (),
+            }
+        }
+
+        // Avoid unnecessary `walk_*` calls.
+        fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {}
+        fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {}
+        fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {}
+        // Avoid monomorphising all `visit_*` functions.
+        fn visit_nested_item(&mut self, _: ItemId) {}
+    }
+    let mut v = V { f, res: None };
+    node.visit(&mut v);
+    v.res
+}
 
 /// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
 /// bodies (i.e. closures) are visited.
@@ -617,3 +685,49 @@ pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx
     })
     .is_break()
 }
+
+/// Runs the given function for each path expression referencing the given local which occur after
+/// the given expression.
+pub fn for_each_local_assignment<'tcx, B>(
+    cx: &LateContext<'tcx>,
+    local_id: HirId,
+    f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>,
+) -> ControlFlow<B> {
+    struct V<'cx, 'tcx, F, B> {
+        cx: &'cx LateContext<'tcx>,
+        local_id: HirId,
+        res: ControlFlow<B>,
+        f: F,
+    }
+    impl<'cx, 'tcx, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B>, B> Visitor<'tcx> for V<'cx, 'tcx, F, B> {
+        type NestedFilter = nested_filter::OnlyBodies;
+        fn nested_visit_map(&mut self) -> Self::Map {
+            self.cx.tcx.hir()
+        }
+
+        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
+            if let ExprKind::Assign(lhs, rhs, _) = e.kind
+                && self.res.is_continue()
+                && path_to_local_id(lhs, self.local_id)
+            {
+                self.res = (self.f)(rhs);
+                self.visit_expr(rhs);
+            } else {
+                walk_expr(self, e);
+            }
+        }
+    }
+
+    if let Some(b) = get_enclosing_block(cx, local_id) {
+        let mut v = V {
+            cx,
+            local_id,
+            res: ControlFlow::Continue(()),
+            f,
+        };
+        v.visit_block(b);
+        v.res
+    } else {
+        ControlFlow::Continue(())
+    }
+}
index 504d58b5197a105b4da50e5e3c43149dd29be060..737c845c045153958cb8f542e24e3b9c4d21772d 100644 (file)
@@ -1,7 +1,7 @@
 [package]
 name = "lintcheck"
 version = "0.0.1"
-description = "tool to monitor impact of changes in Clippys lints on a part of the ecosystem"
+description = "tool to monitor impact of changes in Clippy's lints on a part of the ecosystem"
 readme = "README.md"
 license = "MIT OR Apache-2.0"
 repository = "https://github.com/rust-lang/rust-clippy"
@@ -11,7 +11,7 @@ publish = false
 
 [dependencies]
 cargo_metadata = "0.14"
-clap = "3.1"
+clap = "3.2"
 flate2 = "1.0"
 rayon = "1.5.1"
 serde = { version = "1.0", features = ["derive"] }
index 8c169506e533d7b7892ff558924ca46c784529ca..6f3d23382ce15d7036855a029f882035ecc3c6fe 100644 (file)
@@ -70,7 +70,7 @@ is explicitly specified in the options.
 
 ### Fix mode
 You can run `./lintcheck/target/debug/lintcheck --fix` which will run Clippy with `--fix` and
-print a warning if Clippys suggestions fail to apply (if the resulting code does not build).  
+print a warning if Clippy's suggestions fail to apply (if the resulting code does not build).  
 This lets us spot bad suggestions or false positives automatically in some cases.  
 
 Please note that the target dir should be cleaned afterwards since clippy will modify
index dff9d27db0a6c45f54f7fcab3e090fc644e24c31..9ee25280f046fdcbcf437a674109d4e38bf4614c 100644 (file)
@@ -545,7 +545,7 @@ fn lintcheck_needs_rerun(lintcheck_logs_path: &Path) -> bool {
 fn main() {
     // assert that we launch lintcheck from the repo root (via cargo lintcheck)
     if std::fs::metadata("lintcheck/Cargo.toml").is_err() {
-        eprintln!("lintcheck needs to be run from clippys repo root!\nUse `cargo lintcheck` alternatively.");
+        eprintln!("lintcheck needs to be run from clippy's repo root!\nUse `cargo lintcheck` alternatively.");
         std::process::exit(3);
     }
 
@@ -586,7 +586,7 @@ fn main() {
         .map(|o| String::from_utf8_lossy(&o.stdout).into_owned())
         .expect("could not get clippy version!");
 
-    // download and extract the crates, then run clippy on them and collect clippys warnings
+    // download and extract the crates, then run clippy on them and collect clippy's warnings
     // flatten into one big list of warnings
 
     let crates = read_crates(&config.sources_toml_path);
index 6cc6d5036b379261f729c64128a5f4c68c604cf6..e693e6837592fcaf0cf446016f11fdd3c6c173f2 100644 (file)
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2022-06-30"
+channel = "nightly-2022-07-15"
 components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
index bf7a39edf4c91e72d1c8e2bd04417d173b7ba9fb..3615d07154dfb2d44d1f78c101475d0896675fd4 100644 (file)
@@ -394,6 +394,7 @@ fn compile_test() {
     "single_component_path_imports_nested_first.rs",
     "string_add.rs",
     "toplevel_ref_arg_non_rustfix.rs",
+    "trait_duplication_in_bounds.rs",
     "unit_arg.rs",
     "unnecessary_clone.rs",
     "unnecessary_lazy_eval_unfixable.rs",
index fffc53603424ba831f52c94c14be81ed01af4b00..5697e8680cd6f86779a303edfd5b63d940e8da14 100644 (file)
@@ -74,10 +74,16 @@ fn run_clippy_for_package(project: &str, args: &[&str]) {
         .env("CARGO_INCREMENTAL", "0")
         .arg("clippy")
         .arg("--all-targets")
-        .arg("--all-features")
-        .arg("--")
-        .args(args)
-        .arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
+        .arg("--all-features");
+
+    if let Ok(dogfood_args) = std::env::var("__CLIPPY_DOGFOOD_ARGS") {
+        for arg in dogfood_args.split_whitespace() {
+            command.arg(arg);
+        }
+    }
+
+    command.arg("--").args(args);
+    command.arg("-Cdebuginfo=0"); // disable debuginfo to generate less data in the target dir
 
     if cfg!(feature = "internal") {
         // internal lints only exist if we build with the internal feature
index dd1d441203600cf6f3d124177a4ef29f1ea0d67c..c3aae1a9aa2d01992ee0c00b6c847bc869b033d0 100644 (file)
@@ -1,8 +1,10 @@
+#![feature(once_cell)]
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
 use std::ffi::OsStr;
 use std::path::PathBuf;
+use std::sync::LazyLock;
 
 use regex::RegexSet;
 
@@ -14,42 +16,45 @@ struct Message {
 
 impl Message {
     fn new(path: PathBuf) -> Self {
-        let content: String = std::fs::read_to_string(&path).unwrap();
         // we don't want the first letter after "error: ", "help: " ... to be capitalized
         // also no punctuation (except for "?" ?) at the end of a line
-        let regex_set: RegexSet = RegexSet::new(&[
-            r"error: [A-Z]",
-            r"help: [A-Z]",
-            r"warning: [A-Z]",
-            r"note: [A-Z]",
-            r"try this: [A-Z]",
-            r"error: .*[.!]$",
-            r"help: .*[.!]$",
-            r"warning: .*[.!]$",
-            r"note: .*[.!]$",
-            r"try this: .*[.!]$",
-        ])
-        .unwrap();
+        static REGEX_SET: LazyLock<RegexSet> = LazyLock::new(|| {
+            RegexSet::new(&[
+                r"error: [A-Z]",
+                r"help: [A-Z]",
+                r"warning: [A-Z]",
+                r"note: [A-Z]",
+                r"try this: [A-Z]",
+                r"error: .*[.!]$",
+                r"help: .*[.!]$",
+                r"warning: .*[.!]$",
+                r"note: .*[.!]$",
+                r"try this: .*[.!]$",
+            ])
+            .unwrap()
+        });
 
         // sometimes the first character is capitalized and it is legal (like in "C-like enum variants") or
         // we want to ask a question ending in "?"
-        let exceptions_set: RegexSet = RegexSet::new(&[
-            r".*C-like enum variant discriminant is not portable to 32-bit targets",
-            r".*did you mean `unix`?",
-            r".*the arguments may be inverted...",
-            r".*Intel x86 assembly syntax used",
-            r".*AT&T x86 assembly syntax used",
-            r".*remove .*the return type...",
-            r"note: Clippy version: .*",
-            r"the compiler unexpectedly panicked. this is a bug.",
-        ])
-        .unwrap();
+        static EXCEPTIONS_SET: LazyLock<RegexSet> = LazyLock::new(|| {
+            RegexSet::new(&[
+                r"\.\.\.$",
+                r".*C-like enum variant discriminant is not portable to 32-bit targets",
+                r".*Intel x86 assembly syntax used",
+                r".*AT&T x86 assembly syntax used",
+                r"note: Clippy version: .*",
+                r"the compiler unexpectedly panicked. this is a bug.",
+            ])
+            .unwrap()
+        });
+
+        let content: String = std::fs::read_to_string(&path).unwrap();
 
         let bad_lines = content
             .lines()
-            .filter(|line| regex_set.matches(line).matched_any())
+            .filter(|line| REGEX_SET.matches(line).matched_any())
             // ignore exceptions
-            .filter(|line| !exceptions_set.matches(line).matched_any())
+            .filter(|line| !EXCEPTIONS_SET.matches(line).matched_any())
             .map(ToOwned::to_owned)
             .collect::<Vec<String>>();
 
index 99ca538b6e4a58bcecd21c2ea8bc7dc04037a9d5..6478e65ac81ad04b0ac79c4a553fc1a0486d8213 100644 (file)
@@ -1,4 +1,4 @@
-#[feature(lint_reasons)]
+#![feature(lint_reasons)]
 
 mod a;
 
@@ -25,5 +25,4 @@
 #[allow(clippy::duplicate_mod)]
 mod d4;
 
-
 fn main() {}
index 61df1ad5d501aad6d95ea089f282af0e639d7ce0..b450a2b18f25713eefe7c86857fe963ccf174c7e 100644 (file)
@@ -24,6 +24,17 @@ LL | | mod c3;
    |
    = help: replace all but one `mod` item with `use` items
 
+error: file is loaded as a module multiple times: `$DIR/d.rs`
+  --> $DIR/main.rs:18:1
+   |
+LL |   mod d;
+   |   ^^^^^^ first loaded here
+LL | / #[path = "d.rs"]
+LL | | mod d2;
+   | |_______^ loaded again here
+   |
+   = help: replace all but one `mod` item with `use` items
+
 error: file is loaded as a module multiple times: `$DIR/from_other_module.rs`
   --> $DIR/main.rs:15:1
    |
@@ -38,16 +49,5 @@ LL | | mod m;
    |
    = help: replace all but one `mod` item with `use` items
 
-error: file is loaded as a module multiple times: `$DIR/b.rs`
-  --> $DIR/main.rs:18:1
-   |
-LL |   mod d;
-   |   ^^^^^^ first loaded here
-LL | / #[path = "d.rs"]
-LL | | mod d2;
-   | |_______^ loaded again here
-   |
-   = help: replace all but one `mod` item with `use` items
-
 error: aborting due to 4 previous errors
 
index 5bd2c2799f03683cab8c787f8a5c827616e12bfc..83a0af6b87acad985fad701b01885bf3ba9f838c 100644 (file)
@@ -135,3 +135,8 @@ macro_rules! manual_rem_euclid {
         let _: i32 = ((value % 4) + 4) % 4;
     };
 }
+
+#[macro_export]
+macro_rules! equatable_if_let {
+    ($a:ident) => {{ if let 2 = $a {} }};
+}
index e00f061f28a94b67a91f87d105b43a4131d68bc0..1a74cdb3ff65926ab94e679bdcbea64d4e51db0f 100644 (file)
@@ -6,7 +6,7 @@
     unused
 )]
 
-use std::collections::HashMap;
+use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
 
 macro_rules! boxit {
     ($init:expr, $x:ty) => {
@@ -18,7 +18,7 @@ fn test_macro() {
     boxit!(Vec::new(), Vec<u8>);
 }
 
-fn test(foo: Box<Vec<bool>>) {}
+fn test1(foo: Box<Vec<bool>>) {}
 
 fn test2(foo: Box<dyn Fn(Vec<u32>)>) {
     // pass if #31 is fixed
@@ -29,6 +29,18 @@ fn test3(foo: Box<String>) {}
 
 fn test4(foo: Box<HashMap<String, String>>) {}
 
+fn test5(foo: Box<HashSet<i64>>) {}
+
+fn test6(foo: Box<VecDeque<i32>>) {}
+
+fn test7(foo: Box<LinkedList<i16>>) {}
+
+fn test8(foo: Box<BTreeMap<i8, String>>) {}
+
+fn test9(foo: Box<BTreeSet<u64>>) {}
+
+fn test10(foo: Box<BinaryHeap<u32>>) {}
+
 fn test_local_not_linted() {
     let _: Box<Vec<bool>>;
 }
index 6de85d05a99f32b59eac0905a7f19967d8419f6e..2b28598ded92f41bc2d000d4fbb03dd1f674b074 100644 (file)
@@ -1,8 +1,8 @@
 error: you seem to be trying to use `Box<Vec<..>>`. Consider using just `Vec<..>`
-  --> $DIR/box_collection.rs:21:14
+  --> $DIR/box_collection.rs:21:15
    |
-LL | fn test(foo: Box<Vec<bool>>) {}
-   |              ^^^^^^^^^^^^^^
+LL | fn test1(foo: Box<Vec<bool>>) {}
+   |               ^^^^^^^^^^^^^^
    |
    = note: `-D clippy::box-collection` implied by `-D warnings`
    = help: `Vec<..>` is already on the heap, `Box<Vec<..>>` makes an extra allocation
@@ -23,5 +23,53 @@ LL | fn test4(foo: Box<HashMap<String, String>>) {}
    |
    = help: `HashMap<..>` is already on the heap, `Box<HashMap<..>>` makes an extra allocation
 
-error: aborting due to 3 previous errors
+error: you seem to be trying to use `Box<HashSet<..>>`. Consider using just `HashSet<..>`
+  --> $DIR/box_collection.rs:32:15
+   |
+LL | fn test5(foo: Box<HashSet<i64>>) {}
+   |               ^^^^^^^^^^^^^^^^^
+   |
+   = help: `HashSet<..>` is already on the heap, `Box<HashSet<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<VecDeque<..>>`. Consider using just `VecDeque<..>`
+  --> $DIR/box_collection.rs:34:15
+   |
+LL | fn test6(foo: Box<VecDeque<i32>>) {}
+   |               ^^^^^^^^^^^^^^^^^^
+   |
+   = help: `VecDeque<..>` is already on the heap, `Box<VecDeque<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<LinkedList<..>>`. Consider using just `LinkedList<..>`
+  --> $DIR/box_collection.rs:36:15
+   |
+LL | fn test7(foo: Box<LinkedList<i16>>) {}
+   |               ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: `LinkedList<..>` is already on the heap, `Box<LinkedList<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<BTreeMap<..>>`. Consider using just `BTreeMap<..>`
+  --> $DIR/box_collection.rs:38:15
+   |
+LL | fn test8(foo: Box<BTreeMap<i8, String>>) {}
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: `BTreeMap<..>` is already on the heap, `Box<BTreeMap<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<BTreeSet<..>>`. Consider using just `BTreeSet<..>`
+  --> $DIR/box_collection.rs:40:15
+   |
+LL | fn test9(foo: Box<BTreeSet<u64>>) {}
+   |               ^^^^^^^^^^^^^^^^^^
+   |
+   = help: `BTreeSet<..>` is already on the heap, `Box<BTreeSet<..>>` makes an extra allocation
+
+error: you seem to be trying to use `Box<BinaryHeap<..>>`. Consider using just `BinaryHeap<..>`
+  --> $DIR/box_collection.rs:42:16
+   |
+LL | fn test10(foo: Box<BinaryHeap<u32>>) {}
+   |                ^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: `BinaryHeap<..>` is already on the heap, `Box<BinaryHeap<..>>` makes an extra allocation
+
+error: aborting due to 9 previous errors
 
index 064482009517a600b68be2592ac5de70e0161c8a..5e3a1a29693f58ae5e392e222d6fb9b467216ee3 100644 (file)
@@ -1,6 +1,8 @@
 #![allow(dead_code)]
 #![deny(clippy::if_same_then_else, clippy::branches_sharing_code)]
 
+use std::sync::Mutex;
+
 // ##################################
 // # Issue clippy#7369
 // ##################################
@@ -38,4 +40,56 @@ fn main() {
         let (y, x) = x;
         foo(x, y)
     };
+
+    let m = Mutex::new(0u32);
+    let l = m.lock().unwrap();
+    let _ = if true {
+        drop(l);
+        println!("foo");
+        m.lock().unwrap();
+        0
+    } else if *l == 0 {
+        drop(l);
+        println!("foo");
+        println!("bar");
+        m.lock().unwrap();
+        1
+    } else {
+        drop(l);
+        println!("foo");
+        println!("baz");
+        m.lock().unwrap();
+        2
+    };
+
+    if true {
+        let _guard = m.lock();
+        println!("foo");
+    } else {
+        println!("foo");
+    }
+
+    if true {
+        let _guard = m.lock();
+        println!("foo");
+        println!("bar");
+    } else {
+        let _guard = m.lock();
+        println!("foo");
+        println!("baz");
+    }
+
+    let mut c = 0;
+    for _ in 0..5 {
+        if c == 0 {
+            c += 1;
+            println!("0");
+        } else if c == 1 {
+            c += 1;
+            println!("1");
+        } else {
+            c += 1;
+            println!("more");
+        }
+    }
 }
index 7125f741c150e764ba59141368aba6e6b3d303e0..8990c3ba739b0fea45b8b907b38798eaa7bec42b 100644 (file)
@@ -114,5 +114,5 @@ LL |     3_999_999_999usize as f64;
    |
    = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
 
-error: aborting due to 20 previous errors
+error: aborting due to 18 previous errors
 
diff --git a/src/tools/clippy/tests/ui/crashes/ice-9041.rs b/src/tools/clippy/tests/ui/crashes/ice-9041.rs
new file mode 100644 (file)
index 0000000..55cc9bc
--- /dev/null
@@ -0,0 +1,8 @@
+pub struct Thing;
+
+pub fn has_thing(things: &[Thing]) -> bool {
+    let is_thing_ready = |_peer: &Thing| -> bool { todo!() };
+    things.iter().find(|p| is_thing_ready(p)).is_some()
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-9041.stderr b/src/tools/clippy/tests/ui/crashes/ice-9041.stderr
new file mode 100644 (file)
index 0000000..f5038f0
--- /dev/null
@@ -0,0 +1,10 @@
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/ice-9041.rs:5:19
+   |
+LL |     things.iter().find(|p| is_thing_ready(p)).is_some()
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|p| is_thing_ready(&p))`
+   |
+   = note: `-D clippy::search-is-some` implied by `-D warnings`
+
+error: aborting due to previous error
+
index 47bf25e409bd26e9a0ebc7f66da008474a2552a9..687efdada6e311f4efdf629ebfd0b46ac3bfa388 100644 (file)
@@ -1,8 +1,12 @@
 // run-rustfix
+// aux-build:macro_rules.rs
 
 #![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)]
 #![warn(clippy::equatable_if_let)]
 
+#[macro_use]
+extern crate macro_rules;
+
 use std::cmp::Ordering;
 
 #[derive(PartialEq)]
@@ -75,4 +79,6 @@ fn main() {
     if "abc" == m1!(x) {
         println!("OK");
     }
+
+    equatable_if_let!(a);
 }
index d498bca2455bd971afc750e34b5b90f0202301c7..8c467d14d2a9fb8028406835235a34483ec02c47 100644 (file)
@@ -1,8 +1,12 @@
 // run-rustfix
+// aux-build:macro_rules.rs
 
 #![allow(unused_variables, dead_code, clippy::derive_partial_eq_without_eq)]
 #![warn(clippy::equatable_if_let)]
 
+#[macro_use]
+extern crate macro_rules;
+
 use std::cmp::Ordering;
 
 #[derive(PartialEq)]
@@ -75,4 +79,6 @@ macro_rules! m1 {
     if let m1!(x) = "abc" {
         println!("OK");
     }
+
+    equatable_if_let!(a);
 }
index 760ff88f448f094561f008b0e29be43824306260..9c4c3cc3682e631d7930d7c9b265b6ebab8afed0 100644 (file)
@@ -1,5 +1,5 @@
 error: this pattern matching can be expressed using equality
-  --> $DIR/equatable_if_let.rs:49:8
+  --> $DIR/equatable_if_let.rs:53:8
    |
 LL |     if let 2 = a {}
    |        ^^^^^^^^^ help: try: `a == 2`
@@ -7,61 +7,61 @@ LL |     if let 2 = a {}
    = note: `-D clippy::equatable-if-let` implied by `-D warnings`
 
 error: this pattern matching can be expressed using equality
-  --> $DIR/equatable_if_let.rs:50:8
+  --> $DIR/equatable_if_let.rs:54:8
    |
 LL |     if let Ordering::Greater = a.cmp(&b) {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `a.cmp(&b) == Ordering::Greater`
 
 error: this pattern matching can be expressed using equality
-  --> $DIR/equatable_if_let.rs:51:8
+  --> $DIR/equatable_if_let.rs:55:8
    |
 LL |     if let Some(2) = c {}
    |        ^^^^^^^^^^^^^^^ help: try: `c == Some(2)`
 
 error: this pattern matching can be expressed using equality
-  --> $DIR/equatable_if_let.rs:52:8
+  --> $DIR/equatable_if_let.rs:56:8
    |
 LL |     if let Struct { a: 2, b: false } = d {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `d == (Struct { a: 2, b: false })`
 
 error: this pattern matching can be expressed using equality
-  --> $DIR/equatable_if_let.rs:53:8
+  --> $DIR/equatable_if_let.rs:57:8
    |
 LL |     if let Enum::TupleVariant(32, 64) = e {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::TupleVariant(32, 64)`
 
 error: this pattern matching can be expressed using equality
-  --> $DIR/equatable_if_let.rs:54:8
+  --> $DIR/equatable_if_let.rs:58:8
    |
 LL |     if let Enum::RecordVariant { a: 64, b: 32 } = e {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == (Enum::RecordVariant { a: 64, b: 32 })`
 
 error: this pattern matching can be expressed using equality
-  --> $DIR/equatable_if_let.rs:55:8
+  --> $DIR/equatable_if_let.rs:59:8
    |
 LL |     if let Enum::UnitVariant = e {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `e == Enum::UnitVariant`
 
 error: this pattern matching can be expressed using equality
-  --> $DIR/equatable_if_let.rs:56:8
+  --> $DIR/equatable_if_let.rs:60:8
    |
 LL |     if let (Enum::UnitVariant, &Struct { a: 2, b: false }) = (e, &d) {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(e, &d) == (Enum::UnitVariant, &Struct { a: 2, b: false })`
 
 error: this pattern matching can be expressed using equality
-  --> $DIR/equatable_if_let.rs:66:8
+  --> $DIR/equatable_if_let.rs:70:8
    |
 LL |     if let NotStructuralEq::A = g {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `g == NotStructuralEq::A`
 
 error: this pattern matching can be expressed using equality
-  --> $DIR/equatable_if_let.rs:68:8
+  --> $DIR/equatable_if_let.rs:72:8
    |
 LL |     if let Some(NotStructuralEq::A) = Some(g) {}
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(g) == Some(NotStructuralEq::A)`
 
 error: this pattern matching can be expressed using equality
-  --> $DIR/equatable_if_let.rs:75:8
+  --> $DIR/equatable_if_let.rs:79:8
    |
 LL |     if let m1!(x) = "abc" {
    |        ^^^^^^^^^^^^^^^^^^ help: try: `"abc" == m1!(x)`
index d4ff1b1566dc613bd50d9c46e1f3c5ccdb4d5b64..a650fdc1f897256ab98026823f52aaeec250c19e 100644 (file)
@@ -211,4 +211,8 @@ fn main() {
     unsafe {
         var(0, &**x);
     }
+
+    let s = &"str";
+    let _ = || return *s;
+    let _ = || -> &'static str { return s };
 }
index 99294a7947bf16e8d24939434a085e1384fccbf6..8f4f352576a734514f2648b826205efc4eb219e9 100644 (file)
@@ -211,4 +211,8 @@ fn _f5(x: &u32) -> u32 {
     unsafe {
         var(0, &**x);
     }
+
+    let s = &"str";
+    let _ = || return *s;
+    let _ = || -> &'static str { return *s };
 }
index 55f956e37aed77fb51d97e222658614a9e2ccff6..92765307ea73d55f30a30ecdbc984ba42a645066 100644 (file)
@@ -192,5 +192,11 @@ error: deref which would be done by auto-deref
 LL |     f_str(&&**ref_str); // `needless_borrow` will suggest removing only one reference
    |            ^^^^^^^^^^ help: try this: `ref_str`
 
-error: aborting due to 32 previous errors
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:217:41
+   |
+LL |     let _ = || -> &'static str { return *s };
+   |                                         ^^ help: try this: `s`
+
+error: aborting due to 33 previous errors
 
index 58feae422a3c121f3d89cc14278147037191a99a..6cbfafbb38b9913e7ec71de26f6206fce73ae1cf 100644 (file)
@@ -27,7 +27,7 @@ fn if_let_option() {
     };
 }
 
-// When mutexs are different don't warn
+// When mutexes are different don't warn
 fn if_let_different_mutex() {
     let m = Mutex::new(Some(0_u8));
     let other = Mutex::new(None::<u8>);
index eb66d1afddce39dfce67d421a7229232b52b2b6f..74ba2f1c5e70bd09c67e02c39d2c52a076bc8164 100644 (file)
@@ -36,7 +36,7 @@ mod without_base {
         // issue #7069.
         new_foo!();
 
-        // Shoule NOT lint because the order is the same as in the definition.
+        // Should NOT lint because the order is the same as in the definition.
         Foo { x, y, z };
 
         // Should NOT lint because z is not a shorthand init.
index 5caadc7c62083fb2b214df04ded3b16e2bb665f7..ba96e1e330f5fc62f3e408036a3897d2d6d28a24 100644 (file)
@@ -36,7 +36,7 @@ fn test() {
         // issue #7069.
         new_foo!();
 
-        // Shoule NOT lint because the order is the same as in the definition.
+        // Should NOT lint because the order is the same as in the definition.
         Foo { x, y, z };
 
         // Should NOT lint because z is not a shorthand init.
diff --git a/src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.rs b/src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.rs
new file mode 100644 (file)
index 0000000..3dc096d
--- /dev/null
@@ -0,0 +1,20 @@
+#![warn(clippy::invalid_utf8_in_unchecked)]
+
+fn main() {
+    // Valid
+    unsafe {
+        std::str::from_utf8_unchecked(&[99, 108, 105, 112, 112, 121]);
+        std::str::from_utf8_unchecked(&[b'c', b'l', b'i', b'p', b'p', b'y']);
+        std::str::from_utf8_unchecked(b"clippy");
+
+        let x = 0xA0;
+        std::str::from_utf8_unchecked(&[0xC0, x]);
+    }
+
+    // Invalid
+    unsafe {
+        std::str::from_utf8_unchecked(&[99, 108, 130, 105, 112, 112, 121]);
+        std::str::from_utf8_unchecked(&[b'c', b'l', b'\x82', b'i', b'p', b'p', b'y']);
+        std::str::from_utf8_unchecked(b"cl\x82ippy");
+    }
+}
diff --git a/src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.stderr b/src/tools/clippy/tests/ui/invalid_utf8_in_unchecked.stderr
new file mode 100644 (file)
index 0000000..c89cd27
--- /dev/null
@@ -0,0 +1,22 @@
+error: non UTF-8 literal in `std::str::from_utf8_unchecked`
+  --> $DIR/invalid_utf8_in_unchecked.rs:16:9
+   |
+LL |         std::str::from_utf8_unchecked(&[99, 108, 130, 105, 112, 112, 121]);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::invalid-utf8-in-unchecked` implied by `-D warnings`
+
+error: non UTF-8 literal in `std::str::from_utf8_unchecked`
+  --> $DIR/invalid_utf8_in_unchecked.rs:17:9
+   |
+LL |         std::str::from_utf8_unchecked(&[b'c', b'l', b'/x82', b'i', b'p', b'p', b'y']);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: non UTF-8 literal in `std::str::from_utf8_unchecked`
+  --> $DIR/invalid_utf8_in_unchecked.rs:18:9
+   |
+LL |         std::str::from_utf8_unchecked(b"cl/x82ippy");
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
index e72b746232551b87861e2783cf5ce3d7d76dfd21..6343cff0f7ff941e2c55b0e762b41186c6429048 100644 (file)
@@ -1,8 +1,8 @@
 // run-rustfix
 
+#![feature(lint_reasons)]
 #![warn(clippy::let_unit_value)]
-#![allow(clippy::no_effect)]
-#![allow(unused_variables)]
+#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)]
 
 macro_rules! let_and_return {
     ($n:expr) => {{
@@ -72,8 +72,8 @@ fn _returns_generic() {
     fn f3<T>(x: T) -> T {
         x
     }
-    fn f4<T>(mut x: Vec<T>) -> T {
-        x.pop().unwrap()
+    fn f5<T: Default>(x: bool) -> Option<T> {
+        x.then(|| T::default())
     }
 
     let _: () = f(); // Ok
@@ -85,8 +85,12 @@ fn _returns_generic() {
     f3(()); // Lint
     f3(()); // Lint
 
-    f4(vec![()]); // Lint
-    f4(vec![()]); // Lint
+    // Should lint:
+    // fn f4<T>(mut x: Vec<T>) -> T {
+    //    x.pop().unwrap()
+    // }
+    // let _: () = f4(vec![()]);
+    // let x: () = f4(vec![()]);
 
     // Ok
     let _: () = {
@@ -112,4 +116,62 @@ fn _returns_generic() {
         Some(1) => f2(3),
         Some(_) => (),
     };
+
+    let _: () = f5(true).unwrap();
+
+    #[allow(clippy::let_unit_value)]
+    {
+        let x = f();
+        let y;
+        let z;
+        match 0 {
+            0 => {
+                y = f();
+                z = f();
+            },
+            1 => {
+                println!("test");
+                y = f();
+                z = f3(());
+            },
+            _ => panic!(),
+        }
+
+        let x1;
+        let x2;
+        if true {
+            x1 = f();
+            x2 = x1;
+        } else {
+            x2 = f();
+            x1 = x2;
+        }
+
+        let opt;
+        match f5(true) {
+            Some(x) => opt = x,
+            None => panic!(),
+        };
+
+        #[warn(clippy::let_unit_value)]
+        {
+            let _: () = x;
+            let _: () = y;
+            z;
+            let _: () = x1;
+            let _: () = x2;
+            let _: () = opt;
+        }
+    }
+
+    let () = f();
+}
+
+fn attributes() {
+    fn f() {}
+
+    #[allow(clippy::let_unit_value)]
+    let _ = f();
+    #[expect(clippy::let_unit_value)]
+    let _ = f();
 }
index 47ee0a76724792445b8da0bd6465355b43fafb73..c9bb2849f5cf138f1f944182328d4d000a4957e8 100644 (file)
@@ -1,8 +1,8 @@
 // run-rustfix
 
+#![feature(lint_reasons)]
 #![warn(clippy::let_unit_value)]
-#![allow(clippy::no_effect)]
-#![allow(unused_variables)]
+#![allow(unused, clippy::no_effect, clippy::needless_late_init, path_statements)]
 
 macro_rules! let_and_return {
     ($n:expr) => {{
@@ -72,8 +72,8 @@ fn f2<T, U>(_: T) -> U {
     fn f3<T>(x: T) -> T {
         x
     }
-    fn f4<T>(mut x: Vec<T>) -> T {
-        x.pop().unwrap()
+    fn f5<T: Default>(x: bool) -> Option<T> {
+        x.then(|| T::default())
     }
 
     let _: () = f(); // Ok
@@ -85,8 +85,12 @@ fn f4<T>(mut x: Vec<T>) -> T {
     let _: () = f3(()); // Lint
     let x: () = f3(()); // Lint
 
-    let _: () = f4(vec![()]); // Lint
-    let x: () = f4(vec![()]); // Lint
+    // Should lint:
+    // fn f4<T>(mut x: Vec<T>) -> T {
+    //    x.pop().unwrap()
+    // }
+    // let _: () = f4(vec![()]);
+    // let x: () = f4(vec![()]);
 
     // Ok
     let _: () = {
@@ -112,4 +116,62 @@ fn f4<T>(mut x: Vec<T>) -> T {
         Some(1) => f2(3),
         Some(_) => (),
     };
+
+    let _: () = f5(true).unwrap();
+
+    #[allow(clippy::let_unit_value)]
+    {
+        let x = f();
+        let y;
+        let z;
+        match 0 {
+            0 => {
+                y = f();
+                z = f();
+            },
+            1 => {
+                println!("test");
+                y = f();
+                z = f3(());
+            },
+            _ => panic!(),
+        }
+
+        let x1;
+        let x2;
+        if true {
+            x1 = f();
+            x2 = x1;
+        } else {
+            x2 = f();
+            x1 = x2;
+        }
+
+        let opt;
+        match f5(true) {
+            Some(x) => opt = x,
+            None => panic!(),
+        };
+
+        #[warn(clippy::let_unit_value)]
+        {
+            let _: () = x;
+            let _: () = y;
+            let _: () = z;
+            let _: () = x1;
+            let _: () = x2;
+            let _: () = opt;
+        }
+    }
+
+    let () = f();
+}
+
+fn attributes() {
+    fn f() {}
+
+    #[allow(clippy::let_unit_value)]
+    let _ = f();
+    #[expect(clippy::let_unit_value)]
+    let _ = f();
 }
index 45bf67acdb7361b20bea1636c0c8d3477eb8d3c3..49da74ca7e1c7b4d86e2ee0efce674cddae2025b 100644 (file)
@@ -64,19 +64,7 @@ LL |     let x: () = f3(()); // Lint
    |     ^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f3(());`
 
 error: this let-binding has unit value
-  --> $DIR/let_unit.rs:88:5
-   |
-LL |     let _: () = f4(vec![()]); // Lint
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);`
-
-error: this let-binding has unit value
-  --> $DIR/let_unit.rs:89:5
-   |
-LL |     let x: () = f4(vec![()]); // Lint
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: omit the `let` binding: `f4(vec![()]);`
-
-error: this let-binding has unit value
-  --> $DIR/let_unit.rs:98:5
+  --> $DIR/let_unit.rs:102:5
    |
 LL |     let x: () = if true { f() } else { f2(0) }; // Lint
    |     ^^^^-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -84,7 +72,7 @@ LL |     let x: () = if true { f() } else { f2(0) }; // Lint
    |         help: use a wild (`_`) binding: `_`
 
 error: this let-binding has unit value
-  --> $DIR/let_unit.rs:109:5
+  --> $DIR/let_unit.rs:113:5
    |
 LL | /     let _: () = match Some(0) {
 LL | |         None => f2(1),
@@ -104,5 +92,11 @@ LL +         Some(_) => (),
 LL +     };
    |
 
-error: aborting due to 11 previous errors
+error: this let-binding has unit value
+  --> $DIR/let_unit.rs:160:13
+   |
+LL |             let _: () = z;
+   |             ^^^^^^^^^^^^^^ help: omit the `let` binding: `z;`
+
+error: aborting due to 10 previous errors
 
index de0d86148899d4c7be1220c597291729cec6de4a..4936dc9b2e0178885f4a160739fa33ddab95ccdb 100644 (file)
@@ -12,6 +12,32 @@ fn main() {
 
     // is_ok(), unwrap_or()
     let _ = (0..).filter_map(|a| to_res(a).ok());
+
+    let _ = (1..5)
+        .filter_map(|y| *to_ref(to_opt(y)));
+    let _ = (1..5)
+        .filter_map(|y| *to_ref(to_opt(y)));
+
+    let _ = (1..5)
+        .filter_map(|y| to_ref(to_res(y)).ok());
+    let _ = (1..5)
+        .filter_map(|y| to_ref(to_res(y)).ok());
+}
+
+#[rustfmt::skip]
+fn simple_equal() {
+    iter::<Option<&u8>>().find_map(|x| x.cloned());
+    iter::<&Option<&u8>>().find_map(|x| x.cloned());
+    iter::<&Option<String>>().find_map(|x| x.as_deref());
+    iter::<Option<&String>>().find_map(|y| to_ref(y).cloned());
+
+    iter::<Result<u8, ()>>().find_map(|x| x.ok());
+    iter::<&Result<u8, ()>>().find_map(|x| x.ok());
+    iter::<&&Result<u8, ()>>().find_map(|x| x.ok());
+    iter::<Result<&u8, ()>>().find_map(|x| x.cloned().ok());
+    iter::<&Result<&u8, ()>>().find_map(|x| x.cloned().ok());
+    iter::<&Result<String, ()>>().find_map(|x| x.as_deref().ok());
+    iter::<Result<&String, ()>>().find_map(|y| to_ref(y).cloned().ok());
 }
 
 fn no_lint() {
@@ -28,6 +54,10 @@ fn no_lint() {
         .map(|a| to_opt(a).unwrap());
 }
 
+fn iter<T>() -> impl Iterator<Item = T> {
+    std::iter::empty()
+}
+
 fn to_opt<T>(_: T) -> Option<T> {
     unimplemented!()
 }
@@ -36,6 +66,10 @@ fn to_res<T>(_: T) -> Result<T, ()> {
     unimplemented!()
 }
 
+fn to_ref<'a, T>(_: T) -> &'a T {
+    unimplemented!()
+}
+
 struct Issue8920<'a> {
     option_field: Option<String>,
     result_field: Result<String, ()>,
index bd6516f038b29f90e986931fea796df0c45d2ad1..8c67e827b4c35fe6512b8806ddea1bdaff5880d4 100644 (file)
@@ -12,6 +12,36 @@ fn main() {
 
     // is_ok(), unwrap_or()
     let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
+
+    let _ = (1..5)
+        .filter(|&x| to_ref(to_opt(x)).is_some())
+        .map(|y| to_ref(to_opt(y)).unwrap());
+    let _ = (1..5)
+        .filter(|x| to_ref(to_opt(*x)).is_some())
+        .map(|y| to_ref(to_opt(y)).unwrap());
+
+    let _ = (1..5)
+        .filter(|&x| to_ref(to_res(x)).is_ok())
+        .map(|y| to_ref(to_res(y)).unwrap());
+    let _ = (1..5)
+        .filter(|x| to_ref(to_res(*x)).is_ok())
+        .map(|y| to_ref(to_res(y)).unwrap());
+}
+
+#[rustfmt::skip]
+fn simple_equal() {
+    iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+    iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+    iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
+    iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
+
+    iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+    iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+    iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+    iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+    iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+    iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
+    iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
 }
 
 fn no_lint() {
@@ -28,6 +58,10 @@ fn no_lint() {
         .map(|a| to_opt(a).unwrap());
 }
 
+fn iter<T>() -> impl Iterator<Item = T> {
+    std::iter::empty()
+}
+
 fn to_opt<T>(_: T) -> Option<T> {
     unimplemented!()
 }
@@ -36,6 +70,10 @@ fn to_res<T>(_: T) -> Result<T, ()> {
     unimplemented!()
 }
 
+fn to_ref<'a, T>(_: T) -> &'a T {
+    unimplemented!()
+}
+
 struct Issue8920<'a> {
     option_field: Option<String>,
     result_field: Result<String, ()>,
index 465f1b1911017bed9e51509b6a46cac40c3ee744..6e5bbe8f2aaf577c6afab54e692d686d05769b47 100644 (file)
@@ -19,7 +19,107 @@ LL |     let _ = (0..).filter(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_o
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `filter_map(|a| to_res(a).ok())`
 
 error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-  --> $DIR/manual_filter_map.rs:54:10
+  --> $DIR/manual_filter_map.rs:17:10
+   |
+LL |           .filter(|&x| to_ref(to_opt(x)).is_some())
+   |  __________^
+LL | |         .map(|y| to_ref(to_opt(y)).unwrap());
+   | |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+  --> $DIR/manual_filter_map.rs:20:10
+   |
+LL |           .filter(|x| to_ref(to_opt(*x)).is_some())
+   |  __________^
+LL | |         .map(|y| to_ref(to_opt(y)).unwrap());
+   | |____________________________________________^ help: try: `filter_map(|y| *to_ref(to_opt(y)))`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+  --> $DIR/manual_filter_map.rs:24:10
+   |
+LL |           .filter(|&x| to_ref(to_res(x)).is_ok())
+   |  __________^
+LL | |         .map(|y| to_ref(to_res(y)).unwrap());
+   | |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+  --> $DIR/manual_filter_map.rs:27:10
+   |
+LL |           .filter(|x| to_ref(to_res(*x)).is_ok())
+   |  __________^
+LL | |         .map(|y| to_ref(to_res(y)).unwrap());
+   | |____________________________________________^ help: try: `filter_map(|y| to_ref(to_res(y)).ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_filter_map.rs:33:27
+   |
+LL |     iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
+   |
+   = note: `-D clippy::manual-find-map` implied by `-D warnings`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_filter_map.rs:34:28
+   |
+LL |     iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_filter_map.rs:35:31
+   |
+LL |     iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_filter_map.rs:36:31
+   |
+LL |     iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_filter_map.rs:38:30
+   |
+LL |     iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_filter_map.rs:39:31
+   |
+LL |     iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_filter_map.rs:40:32
+   |
+LL |     iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_filter_map.rs:41:31
+   |
+LL |     iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_filter_map.rs:42:32
+   |
+LL |     iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_filter_map.rs:43:35
+   |
+LL |     iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_filter_map.rs:44:35
+   |
+LL |     iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())`
+
+error: `filter(..).map(..)` can be simplified as `filter_map(..)`
+  --> $DIR/manual_filter_map.rs:92:10
    |
 LL |           .filter(|f| f.option_field.is_some())
    |  __________^
@@ -27,7 +127,7 @@ LL | |         .map(|f| f.option_field.clone().unwrap());
    | |_________________________________________________^ help: try: `filter_map(|f| f.option_field.clone())`
 
 error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-  --> $DIR/manual_filter_map.rs:59:10
+  --> $DIR/manual_filter_map.rs:97:10
    |
 LL |           .filter(|f| f.ref_field.is_some())
    |  __________^
@@ -35,7 +135,7 @@ LL | |         .map(|f| f.ref_field.cloned().unwrap());
    | |_______________________________________________^ help: try: `filter_map(|f| f.ref_field.cloned())`
 
 error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-  --> $DIR/manual_filter_map.rs:64:10
+  --> $DIR/manual_filter_map.rs:102:10
    |
 LL |           .filter(|f| f.ref_field.is_some())
    |  __________^
@@ -43,7 +143,7 @@ LL | |         .map(|f| f.ref_field.copied().unwrap());
    | |_______________________________________________^ help: try: `filter_map(|f| f.ref_field.copied())`
 
 error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-  --> $DIR/manual_filter_map.rs:69:10
+  --> $DIR/manual_filter_map.rs:107:10
    |
 LL |           .filter(|f| f.result_field.is_ok())
    |  __________^
@@ -51,7 +151,7 @@ LL | |         .map(|f| f.result_field.clone().unwrap());
    | |_________________________________________________^ help: try: `filter_map(|f| f.result_field.clone().ok())`
 
 error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-  --> $DIR/manual_filter_map.rs:74:10
+  --> $DIR/manual_filter_map.rs:112:10
    |
 LL |           .filter(|f| f.result_field.is_ok())
    |  __________^
@@ -59,7 +159,7 @@ LL | |         .map(|f| f.result_field.as_ref().unwrap());
    | |__________________________________________________^ help: try: `filter_map(|f| f.result_field.as_ref().ok())`
 
 error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-  --> $DIR/manual_filter_map.rs:79:10
+  --> $DIR/manual_filter_map.rs:117:10
    |
 LL |           .filter(|f| f.result_field.is_ok())
    |  __________^
@@ -67,7 +167,7 @@ LL | |         .map(|f| f.result_field.as_deref().unwrap());
    | |____________________________________________________^ help: try: `filter_map(|f| f.result_field.as_deref().ok())`
 
 error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-  --> $DIR/manual_filter_map.rs:84:10
+  --> $DIR/manual_filter_map.rs:122:10
    |
 LL |           .filter(|f| f.result_field.is_ok())
    |  __________^
@@ -75,7 +175,7 @@ LL | |         .map(|f| f.result_field.as_mut().unwrap());
    | |__________________________________________________^ help: try: `filter_map(|f| f.result_field.as_mut().ok())`
 
 error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-  --> $DIR/manual_filter_map.rs:89:10
+  --> $DIR/manual_filter_map.rs:127:10
    |
 LL |           .filter(|f| f.result_field.is_ok())
    |  __________^
@@ -83,12 +183,12 @@ LL | |         .map(|f| f.result_field.as_deref_mut().unwrap());
    | |________________________________________________________^ help: try: `filter_map(|f| f.result_field.as_deref_mut().ok())`
 
 error: `filter(..).map(..)` can be simplified as `filter_map(..)`
-  --> $DIR/manual_filter_map.rs:94:10
+  --> $DIR/manual_filter_map.rs:132:10
    |
 LL |           .filter(|f| f.result_field.is_ok())
    |  __________^
 LL | |         .map(|f| f.result_field.to_owned().unwrap());
    | |____________________________________________________^ help: try: `filter_map(|f| f.result_field.to_owned().ok())`
 
-error: aborting due to 12 previous errors
+error: aborting due to 27 previous errors
 
index d69b6c1dcf3bb7b5ba84cac8d1e9fab1d0fef668..54302beceff628171c4833ba1d3ead35bbfded5d 100644 (file)
@@ -12,6 +12,35 @@ fn main() {
 
     // is_ok(), unwrap_or()
     let _ = (0..).find_map(|a| to_res(a).ok());
+
+    let _ = (1..5)
+        .find_map(|y| *to_ref(to_opt(y)));
+    let _ = (1..5)
+        .find_map(|y| *to_ref(to_opt(y)));
+
+    let _ = (1..5)
+        .find_map(|y| to_ref(to_res(y)).ok());
+    let _ = (1..5)
+        .find_map(|y| to_ref(to_res(y)).ok());
+}
+
+#[rustfmt::skip]
+fn simple_equal() {
+    iter::<Option<u8>>().find_map(|x| x);
+    iter::<&Option<u8>>().find_map(|x| *x);
+    iter::<&&Option<u8>>().find_map(|x| **x);
+    iter::<Option<&u8>>().find_map(|x| x.cloned());
+    iter::<&Option<&u8>>().find_map(|x| x.cloned());
+    iter::<&Option<String>>().find_map(|x| x.as_deref());
+    iter::<Option<&String>>().find_map(|y| to_ref(y).cloned());
+
+    iter::<Result<u8, ()>>().find_map(|x| x.ok());
+    iter::<&Result<u8, ()>>().find_map(|x| x.ok());
+    iter::<&&Result<u8, ()>>().find_map(|x| x.ok());
+    iter::<Result<&u8, ()>>().find_map(|x| x.cloned().ok());
+    iter::<&Result<&u8, ()>>().find_map(|x| x.cloned().ok());
+    iter::<&Result<String, ()>>().find_map(|x| x.as_deref().ok());
+    iter::<Result<&String, ()>>().find_map(|y| to_ref(y).cloned().ok());
 }
 
 fn no_lint() {
@@ -28,6 +57,10 @@ fn no_lint() {
         .map(|a| to_opt(a).unwrap());
 }
 
+fn iter<T>() -> impl Iterator<Item = T> {
+    std::iter::empty()
+}
+
 fn to_opt<T>(_: T) -> Option<T> {
     unimplemented!()
 }
@@ -36,6 +69,10 @@ fn to_res<T>(_: T) -> Result<T, ()> {
     unimplemented!()
 }
 
+fn to_ref<'a, T>(_: T) -> &'a T {
+    unimplemented!()
+}
+
 struct Issue8920<'a> {
     option_field: Option<String>,
     result_field: Result<String, ()>,
index 1c4e18e31c8b1883fef79057675513ec717ff79a..afcc1825a9ac1c9170faa6813c6864f2f7ae6d0e 100644 (file)
@@ -12,6 +12,39 @@ fn main() {
 
     // is_ok(), unwrap_or()
     let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(1));
+
+    let _ = (1..5)
+        .find(|&x| to_ref(to_opt(x)).is_some())
+        .map(|y| to_ref(to_opt(y)).unwrap());
+    let _ = (1..5)
+        .find(|x| to_ref(to_opt(*x)).is_some())
+        .map(|y| to_ref(to_opt(y)).unwrap());
+
+    let _ = (1..5)
+        .find(|&x| to_ref(to_res(x)).is_ok())
+        .map(|y| to_ref(to_res(y)).unwrap());
+    let _ = (1..5)
+        .find(|x| to_ref(to_res(*x)).is_ok())
+        .map(|y| to_ref(to_res(y)).unwrap());
+}
+
+#[rustfmt::skip]
+fn simple_equal() {
+    iter::<Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+    iter::<&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+    iter::<&&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+    iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+    iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+    iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
+    iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
+
+    iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+    iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+    iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+    iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+    iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+    iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
+    iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
 }
 
 fn no_lint() {
@@ -28,6 +61,10 @@ fn no_lint() {
         .map(|a| to_opt(a).unwrap());
 }
 
+fn iter<T>() -> impl Iterator<Item = T> {
+    std::iter::empty()
+}
+
 fn to_opt<T>(_: T) -> Option<T> {
     unimplemented!()
 }
@@ -36,6 +73,10 @@ fn to_res<T>(_: T) -> Result<T, ()> {
     unimplemented!()
 }
 
+fn to_ref<'a, T>(_: T) -> &'a T {
+    unimplemented!()
+}
+
 struct Issue8920<'a> {
     option_field: Option<String>,
     result_field: Result<String, ()>,
index 9dea42b76868b7d838338d55767aeef8da541a5d..c1ac499f7c6010c9ce5f764aef45d01204e38155 100644 (file)
@@ -19,7 +19,123 @@ LL |     let _ = (0..).find(|&n| to_res(n).is_ok()).map(|a| to_res(a).unwrap_or(
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|a| to_res(a).ok())`
 
 error: `find(..).map(..)` can be simplified as `find_map(..)`
-  --> $DIR/manual_find_map.rs:54:10
+  --> $DIR/manual_find_map.rs:17:10
+   |
+LL |           .find(|&x| to_ref(to_opt(x)).is_some())
+   |  __________^
+LL | |         .map(|y| to_ref(to_opt(y)).unwrap());
+   | |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:20:10
+   |
+LL |           .find(|x| to_ref(to_opt(*x)).is_some())
+   |  __________^
+LL | |         .map(|y| to_ref(to_opt(y)).unwrap());
+   | |____________________________________________^ help: try: `find_map(|y| *to_ref(to_opt(y)))`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:24:10
+   |
+LL |           .find(|&x| to_ref(to_res(x)).is_ok())
+   |  __________^
+LL | |         .map(|y| to_ref(to_res(y)).unwrap());
+   | |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:27:10
+   |
+LL |           .find(|x| to_ref(to_res(*x)).is_ok())
+   |  __________^
+LL | |         .map(|y| to_ref(to_res(y)).unwrap());
+   | |____________________________________________^ help: try: `find_map(|y| to_ref(to_res(y)).ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:33:26
+   |
+LL |     iter::<Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x)`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:34:27
+   |
+LL |     iter::<&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| *x)`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:35:28
+   |
+LL |     iter::<&&Option<u8>>().find(|x| x.is_some()).map(|x| x.unwrap());
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| **x)`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:36:27
+   |
+LL |     iter::<Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:37:28
+   |
+LL |     iter::<&Option<&u8>>().find(|x| x.is_some()).map(|x| x.cloned().unwrap());
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:38:31
+   |
+LL |     iter::<&Option<String>>().find(|x| x.is_some()).map(|x| x.as_deref().unwrap());
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:39:31
+   |
+LL |     iter::<Option<&String>>().find(|&x| to_ref(x).is_some()).map(|y| to_ref(y).cloned().unwrap());
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:41:30
+   |
+LL |     iter::<Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:42:31
+   |
+LL |     iter::<&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:43:32
+   |
+LL |     iter::<&&Result<u8, ()>>().find(|x| x.is_ok()).map(|x| x.unwrap());
+   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:44:31
+   |
+LL |     iter::<Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:45:32
+   |
+LL |     iter::<&Result<&u8, ()>>().find(|x| x.is_ok()).map(|x| x.cloned().unwrap());
+   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.cloned().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:46:35
+   |
+LL |     iter::<&Result<String, ()>>().find(|x| x.is_ok()).map(|x| x.as_deref().unwrap());
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|x| x.as_deref().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:47:35
+   |
+LL |     iter::<Result<&String, ()>>().find(|&x| to_ref(x).is_ok()).map(|y| to_ref(y).cloned().unwrap());
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `find_map(|y| to_ref(y).cloned().ok())`
+
+error: `find(..).map(..)` can be simplified as `find_map(..)`
+  --> $DIR/manual_find_map.rs:95:10
    |
 LL |           .find(|f| f.option_field.is_some())
    |  __________^
@@ -27,7 +143,7 @@ LL | |         .map(|f| f.option_field.clone().unwrap());
    | |_________________________________________________^ help: try: `find_map(|f| f.option_field.clone())`
 
 error: `find(..).map(..)` can be simplified as `find_map(..)`
-  --> $DIR/manual_find_map.rs:59:10
+  --> $DIR/manual_find_map.rs:100:10
    |
 LL |           .find(|f| f.ref_field.is_some())
    |  __________^
@@ -35,7 +151,7 @@ LL | |         .map(|f| f.ref_field.cloned().unwrap());
    | |_______________________________________________^ help: try: `find_map(|f| f.ref_field.cloned())`
 
 error: `find(..).map(..)` can be simplified as `find_map(..)`
-  --> $DIR/manual_find_map.rs:64:10
+  --> $DIR/manual_find_map.rs:105:10
    |
 LL |           .find(|f| f.ref_field.is_some())
    |  __________^
@@ -43,7 +159,7 @@ LL | |         .map(|f| f.ref_field.copied().unwrap());
    | |_______________________________________________^ help: try: `find_map(|f| f.ref_field.copied())`
 
 error: `find(..).map(..)` can be simplified as `find_map(..)`
-  --> $DIR/manual_find_map.rs:69:10
+  --> $DIR/manual_find_map.rs:110:10
    |
 LL |           .find(|f| f.result_field.is_ok())
    |  __________^
@@ -51,7 +167,7 @@ LL | |         .map(|f| f.result_field.clone().unwrap());
    | |_________________________________________________^ help: try: `find_map(|f| f.result_field.clone().ok())`
 
 error: `find(..).map(..)` can be simplified as `find_map(..)`
-  --> $DIR/manual_find_map.rs:74:10
+  --> $DIR/manual_find_map.rs:115:10
    |
 LL |           .find(|f| f.result_field.is_ok())
    |  __________^
@@ -59,7 +175,7 @@ LL | |         .map(|f| f.result_field.as_ref().unwrap());
    | |__________________________________________________^ help: try: `find_map(|f| f.result_field.as_ref().ok())`
 
 error: `find(..).map(..)` can be simplified as `find_map(..)`
-  --> $DIR/manual_find_map.rs:79:10
+  --> $DIR/manual_find_map.rs:120:10
    |
 LL |           .find(|f| f.result_field.is_ok())
    |  __________^
@@ -67,7 +183,7 @@ LL | |         .map(|f| f.result_field.as_deref().unwrap());
    | |____________________________________________________^ help: try: `find_map(|f| f.result_field.as_deref().ok())`
 
 error: `find(..).map(..)` can be simplified as `find_map(..)`
-  --> $DIR/manual_find_map.rs:84:10
+  --> $DIR/manual_find_map.rs:125:10
    |
 LL |           .find(|f| f.result_field.is_ok())
    |  __________^
@@ -75,7 +191,7 @@ LL | |         .map(|f| f.result_field.as_mut().unwrap());
    | |__________________________________________________^ help: try: `find_map(|f| f.result_field.as_mut().ok())`
 
 error: `find(..).map(..)` can be simplified as `find_map(..)`
-  --> $DIR/manual_find_map.rs:89:10
+  --> $DIR/manual_find_map.rs:130:10
    |
 LL |           .find(|f| f.result_field.is_ok())
    |  __________^
@@ -83,12 +199,12 @@ LL | |         .map(|f| f.result_field.as_deref_mut().unwrap());
    | |________________________________________________________^ help: try: `find_map(|f| f.result_field.as_deref_mut().ok())`
 
 error: `find(..).map(..)` can be simplified as `find_map(..)`
-  --> $DIR/manual_find_map.rs:94:10
+  --> $DIR/manual_find_map.rs:135:10
    |
 LL |           .find(|f| f.result_field.is_ok())
    |  __________^
 LL | |         .map(|f| f.result_field.to_owned().unwrap());
    | |____________________________________________________^ help: try: `find_map(|f| f.result_field.to_owned().ok())`
 
-error: aborting due to 12 previous errors
+error: aborting due to 30 previous errors
 
index 6c5232ec5f55b7d0e59dec9373f55b4f91cd0f16..d922593bc6f93445ab9b3fc7805692a4da2a6c31 100644 (file)
@@ -106,4 +106,20 @@ struct Test {
     for n in vec![Some(1), Some(2), Some(3)].iter().flatten() {
         println!("{}", n);
     }
+
+    run_unformatted_tests();
+}
+
+#[rustfmt::skip]
+fn run_unformatted_tests() {
+    // Skip rustfmt here on purpose so the suggestion does not fit in one line
+    for n in vec![
+        Some(1),
+        Some(2),
+        Some(3)
+    ].iter() {
+        if let Some(n) = n {
+            println!("{:?}", n);
+        }
+    }
 }
index 392e1a39393754214ce44a4c04bdc222b85cb6b3..da053c0566833618d5497711b314091575480b12 100644 (file)
@@ -167,5 +167,33 @@ LL | |             println!("{:?}", n);
 LL | |         }
    | |_________^
 
-error: aborting due to 8 previous errors
+error: unnecessary `if let` since only the `Some` variant of the iterator element is used
+  --> $DIR/manual_flatten.rs:116:5
+   |
+LL | /     for n in vec![
+LL | |         Some(1),
+LL | |         Some(2),
+LL | |         Some(3)
+...  |
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: remove the `if let` statement in the for loop and then...
+  --> $DIR/manual_flatten.rs:121:9
+   |
+LL | /         if let Some(n) = n {
+LL | |             println!("{:?}", n);
+LL | |         }
+   | |_________^
+help: try
+   |
+LL ~     for n in vec![
+LL +         Some(1),
+LL +         Some(2),
+LL +         Some(3)
+LL ~     ].iter().flatten() {
+   |
+
+error: aborting due to 9 previous errors
 
index e9b41354c58fa50599e93f2b39a5dc015f61646d..312819a0a2cf83d567307d788cb5e8c72394561b 100644 (file)
@@ -34,21 +34,20 @@ fn main() {
 }
 
 fn issue8734() {
-    //     let _ = [0u8, 1, 2, 3]
-    //         .into_iter()
-    //         .map(|n| match n {
-    //             1 => [n
-    //                 .saturating_add(1)
-    //                 .saturating_add(1)
-    //                 .saturating_add(1)
-    //                 .saturating_add(1)
-    //                 .saturating_add(1)
-    //                 .saturating_add(1)
-    //                 .saturating_add(1)
-    //                 .saturating_add(1)],
-    //             n => [n],
-    //         })
-    //         .flatten();
+    let _ = [0u8, 1, 2, 3]
+        .into_iter()
+        .flat_map(|n| match n {
+            1 => [n
+                .saturating_add(1)
+                .saturating_add(1)
+                .saturating_add(1)
+                .saturating_add(1)
+                .saturating_add(1)
+                .saturating_add(1)
+                .saturating_add(1)
+                .saturating_add(1)],
+            n => [n],
+        });
 }
 
 #[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again
@@ -59,7 +58,7 @@ fn issue8878() {
         .and_then(|_| {
 // we need some newlines
 // so that the span is big enough
-// for a splitted output of the diagnostic
+// for a split output of the diagnostic
             Some("")
  // whitespace beforehand is important as well
         });
index 4345c6eee746120afc03cb46fc3e6f79a2d94f40..3fbf4f9a1b044b8c5621a2f73c8ffd7bd39deb84 100644 (file)
@@ -34,21 +34,21 @@ fn option_id(x: i8) -> Option<i8> {
 }
 
 fn issue8734() {
-    //     let _ = [0u8, 1, 2, 3]
-    //         .into_iter()
-    //         .map(|n| match n {
-    //             1 => [n
-    //                 .saturating_add(1)
-    //                 .saturating_add(1)
-    //                 .saturating_add(1)
-    //                 .saturating_add(1)
-    //                 .saturating_add(1)
-    //                 .saturating_add(1)
-    //                 .saturating_add(1)
-    //                 .saturating_add(1)],
-    //             n => [n],
-    //         })
-    //         .flatten();
+    let _ = [0u8, 1, 2, 3]
+        .into_iter()
+        .map(|n| match n {
+            1 => [n
+                .saturating_add(1)
+                .saturating_add(1)
+                .saturating_add(1)
+                .saturating_add(1)
+                .saturating_add(1)
+                .saturating_add(1)
+                .saturating_add(1)
+                .saturating_add(1)],
+            n => [n],
+        })
+        .flatten();
 }
 
 #[allow(clippy::bind_instead_of_map)] // map + flatten will be suggested to `and_then`, but afterwards `map` is suggested again
@@ -59,7 +59,7 @@ fn issue8878() {
         .map(|_| {
 // we need some newlines
 // so that the span is big enough
-// for a splitted output of the diagnostic
+// for a split output of the diagnostic
             Some("")
  // whitespace beforehand is important as well
         })
index f3b82ad08d0fc3c61c31cb53e46c035c06789440..c91f0b9ae94fec2626997de308d60c006d6ffaa4 100644 (file)
@@ -42,6 +42,35 @@ error: called `map(..).flatten()` on `Result`
 LL |     let _: Result<_, &str> = (Ok(Ok(1))).map(|x| x).flatten();
    |                                          ^^^^^^^^^^^^^^^^^^^^ help: try replacing `map` with `and_then` and remove the `.flatten()`: `and_then(|x| x)`
 
+error: called `map(..).flatten()` on `Iterator`
+  --> $DIR/map_flatten_fixable.rs:39:10
+   |
+LL |           .map(|n| match n {
+   |  __________^
+LL | |             1 => [n
+LL | |                 .saturating_add(1)
+LL | |                 .saturating_add(1)
+...  |
+LL | |         })
+LL | |         .flatten();
+   | |__________________^
+   |
+help: try replacing `map` with `flat_map` and remove the `.flatten()`
+   |
+LL ~         .flat_map(|n| match n {
+LL +             1 => [n
+LL +                 .saturating_add(1)
+LL +                 .saturating_add(1)
+LL +                 .saturating_add(1)
+LL +                 .saturating_add(1)
+LL +                 .saturating_add(1)
+LL +                 .saturating_add(1)
+LL +                 .saturating_add(1)
+LL +                 .saturating_add(1)],
+LL +             n => [n],
+LL ~         });
+   |
+
 error: called `map(..).flatten()` on `Option`
   --> $DIR/map_flatten_fixable.rs:59:10
    |
@@ -49,7 +78,7 @@ LL |           .map(|_| {
    |  __________^
 LL | | // we need some newlines
 LL | | // so that the span is big enough
-LL | | // for a splitted output of the diagnostic
+LL | | // for a split output of the diagnostic
 ...  |
 LL | |         })
 LL | |         .flatten();
@@ -60,11 +89,11 @@ help: try replacing `map` with `and_then` and remove the `.flatten()`
 LL ~         .and_then(|_| {
 LL + // we need some newlines
 LL + // so that the span is big enough
-LL + // for a splitted output of the diagnostic
+LL + // for a split output of the diagnostic
 LL +             Some("")
 LL +  // whitespace beforehand is important as well
 LL ~         });
    |
 
-error: aborting due to 8 previous errors
+error: aborting due to 9 previous errors
 
index 36f233f33460720759355d78d1c0cf26623d7713..1ccbfda64b73a5240ae68929f83c37d07cd66c05 100644 (file)
@@ -45,6 +45,12 @@ fn main() {
         // lint
         let _ans = matches!(x, E::A(_) | E::B(_));
     }
+    {
+        // lint
+        // skip rustfmt to prevent removing block for first pattern
+        #[rustfmt::skip]
+        let _ans = matches!(x, E::A(_) | E::B(_));
+    }
     {
         // lint
         let _ans = !matches!(x, E::B(_) | E::C);
index 750f69fa5088f6fa45898030bfca0d56ee486a81..a49991f594174473c51cc41609d3f808b1d80768 100644 (file)
@@ -61,6 +61,18 @@ enum E {
             _ => false,
         };
     }
+    {
+        // lint
+        // skip rustfmt to prevent removing block for first pattern
+        #[rustfmt::skip]
+        let _ans = match x {
+            E::A(_) => {
+                true
+            }
+            E::B(_) => true,
+            _ => false,
+        };
+    }
     {
         // lint
         let _ans = match x {
index d7cedf9f9f1596dfabb659099e2922281b410f93..e94555e27448b0d131c6e34d6d0cb5bdac330b4e 100644 (file)
@@ -60,7 +60,20 @@ LL | |         };
    | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:66:20
+  --> $DIR/match_expr_like_matches_macro.rs:68:20
+   |
+LL |           let _ans = match x {
+   |  ____________________^
+LL | |             E::A(_) => {
+LL | |                 true
+LL | |             }
+LL | |             E::B(_) => true,
+LL | |             _ => false,
+LL | |         };
+   | |_________^ help: try this: `matches!(x, E::A(_) | E::B(_))`
+
+error: match expression looks like `matches!` macro
+  --> $DIR/match_expr_like_matches_macro.rs:78:20
    |
 LL |           let _ans = match x {
    |  ____________________^
@@ -71,7 +84,7 @@ LL | |         };
    | |_________^ help: try this: `!matches!(x, E::B(_) | E::C)`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:126:18
+  --> $DIR/match_expr_like_matches_macro.rs:138:18
    |
 LL |           let _z = match &z {
    |  __________________^
@@ -81,7 +94,7 @@ LL | |         };
    | |_________^ help: try this: `matches!(z, Some(3))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:135:18
+  --> $DIR/match_expr_like_matches_macro.rs:147:18
    |
 LL |           let _z = match &z {
    |  __________________^
@@ -91,7 +104,7 @@ LL | |         };
    | |_________^ help: try this: `matches!(&z, Some(3))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:152:21
+  --> $DIR/match_expr_like_matches_macro.rs:164:21
    |
 LL |               let _ = match &z {
    |  _____________________^
@@ -101,7 +114,7 @@ LL | |             };
    | |_____________^ help: try this: `matches!(&z, AnEnum::X)`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:166:20
+  --> $DIR/match_expr_like_matches_macro.rs:178:20
    |
 LL |           let _res = match &val {
    |  ____________________^
@@ -111,7 +124,7 @@ LL | |         };
    | |_________^ help: try this: `matches!(&val, &Some(ref _a))`
 
 error: match expression looks like `matches!` macro
-  --> $DIR/match_expr_like_matches_macro.rs:178:20
+  --> $DIR/match_expr_like_matches_macro.rs:190:20
    |
 LL |           let _res = match &val {
    |  ____________________^
@@ -120,5 +133,5 @@ LL | |             _ => false,
 LL | |         };
    | |_________^ help: try this: `matches!(&val, &Some(ref _a))`
 
-error: aborting due to 12 previous errors
+error: aborting due to 13 previous errors
 
index cb005122436019f2a2be9ac1c8fb219d8257e534..09afe2ddbbf6289e21d64cb36e1dea36053b51a0 100644 (file)
@@ -115,6 +115,18 @@ fn main() {
         fn foo_ref(&self) {}
     }
     (&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
+
+    struct S;
+    impl From<S> for u32 {
+        fn from(s: S) -> Self {
+            (&s).into()
+        }
+    }
+    impl From<&S> for u32 {
+        fn from(s: &S) -> Self {
+            0
+        }
+    }
 }
 
 #[allow(clippy::needless_borrowed_reference)]
index d636a40100378c0f5bebc5cbb3f85b9f902ab365..3ae4722a1f8985e414ea3a9d49f85ccb397bf770 100644 (file)
@@ -115,6 +115,18 @@ impl FooRef for &'_ () {
         fn foo_ref(&self) {}
     }
     (&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
+
+    struct S;
+    impl From<S> for u32 {
+        fn from(s: S) -> Self {
+            (&s).into()
+        }
+    }
+    impl From<&S> for u32 {
+        fn from(s: &S) -> Self {
+            0
+        }
+    }
 }
 
 #[allow(clippy::needless_borrowed_reference)]
index b997e5316cf3f451442295cc4a776f4bf41a8c3f..0c9178fb85efe39a0a3e7938e658b162fcb2efc1 100644 (file)
@@ -99,6 +99,7 @@ fn if_let_result() {
     let _: Result<i32, i32> = x;
     let _: Result<i32, i32> = x;
     // Input type mismatch, don't trigger
+    #[allow(clippy::question_mark)]
     let _: Result<i32, i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
 }
 
index 90482775a1eebbf7d1fa1621b3c3a233b3739d5a..f66f01d7ccaf4ef2f96be1e2e2247cebf83884f2 100644 (file)
@@ -122,6 +122,7 @@ fn if_let_result() {
     let _: Result<i32, i32> = if let Err(e) = x { Err(e) } else { x };
     let _: Result<i32, i32> = if let Ok(val) = x { Ok(val) } else { x };
     // Input type mismatch, don't trigger
+    #[allow(clippy::question_mark)]
     let _: Result<i32, i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
 }
 
index 2d679631c6f0d71d9da899d9966bb3bb8e595e3c..5bc79800a1a748bcbbb696e6fa824c791af3e647 100644 (file)
@@ -84,7 +84,7 @@ LL |     let _: Result<i32, i32> = if let Ok(val) = x { Ok(val) } else { x };
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
 
 error: this if-let expression is unnecessary
-  --> $DIR/needless_match.rs:129:21
+  --> $DIR/needless_match.rs:130:21
    |
 LL |       let _: Simple = if let Simple::A = x {
    |  _____________________^
@@ -97,7 +97,7 @@ LL | |     };
    | |_____^ help: replace it with: `x`
 
 error: this match expression is unnecessary
-  --> $DIR/needless_match.rs:168:26
+  --> $DIR/needless_match.rs:169:26
    |
 LL |           let _: Complex = match ce {
    |  __________________________^
index 538927b18055a289c771f5cb1cfdc1ba9bfad531..65809023f8dff129d3d3d734c72d09def63b74c9 100644 (file)
@@ -212,3 +212,17 @@ pub fn new() -> Self {
 }
 
 fn main() {}
+
+pub struct IgnoreConstGenericNew(usize);
+impl IgnoreConstGenericNew {
+    pub fn new<const N: usize>() -> Self {
+        Self(N)
+    }
+}
+
+pub struct IgnoreLifetimeNew;
+impl IgnoreLifetimeNew {
+    pub fn new<'a>() -> Self {
+        Self
+    }
+}
index f0c59b4080be3ca78c60631c67ec214afaa9acce..fdefb11ae17a543f4f4c1cabdd4a1d88eddb36ad 100644 (file)
@@ -5,7 +5,6 @@
 #![allow(clippy::deref_addrof)]
 #![allow(clippy::redundant_field_names)]
 
-
 struct Unit;
 struct Tuple(i32);
 struct Struct {
index 06b88bb5bee7ab5c3ef908a1a7efc66950c3bcf3..328d2555ceb8e98304485abf59f08d3ceda9cfaa 100644 (file)
@@ -1,5 +1,5 @@
 error: statement with no effect
-  --> $DIR/no_effect.rs:95:5
+  --> $DIR/no_effect.rs:94:5
    |
 LL |     0;
    |     ^^
@@ -7,157 +7,157 @@ LL |     0;
    = note: `-D clippy::no-effect` implied by `-D warnings`
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:96:5
+  --> $DIR/no_effect.rs:95:5
    |
 LL |     s2;
    |     ^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:97:5
+  --> $DIR/no_effect.rs:96:5
    |
 LL |     Unit;
    |     ^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:98:5
+  --> $DIR/no_effect.rs:97:5
    |
 LL |     Tuple(0);
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:99:5
+  --> $DIR/no_effect.rs:98:5
    |
 LL |     Struct { field: 0 };
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:100:5
+  --> $DIR/no_effect.rs:99:5
    |
 LL |     Struct { ..s };
    |     ^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:101:5
+  --> $DIR/no_effect.rs:100:5
    |
 LL |     Union { a: 0 };
    |     ^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:102:5
+  --> $DIR/no_effect.rs:101:5
    |
 LL |     Enum::Tuple(0);
    |     ^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:103:5
+  --> $DIR/no_effect.rs:102:5
    |
 LL |     Enum::Struct { field: 0 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:104:5
+  --> $DIR/no_effect.rs:103:5
    |
 LL |     5 + 6;
    |     ^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:105:5
+  --> $DIR/no_effect.rs:104:5
    |
 LL |     *&42;
    |     ^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:106:5
+  --> $DIR/no_effect.rs:105:5
    |
 LL |     &6;
    |     ^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:107:5
+  --> $DIR/no_effect.rs:106:5
    |
 LL |     (5, 6, 7);
    |     ^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:108:5
+  --> $DIR/no_effect.rs:107:5
    |
 LL |     box 42;
    |     ^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:109:5
+  --> $DIR/no_effect.rs:108:5
    |
 LL |     ..;
    |     ^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:110:5
+  --> $DIR/no_effect.rs:109:5
    |
 LL |     5..;
    |     ^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:111:5
+  --> $DIR/no_effect.rs:110:5
    |
 LL |     ..5;
    |     ^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:112:5
+  --> $DIR/no_effect.rs:111:5
    |
 LL |     5..6;
    |     ^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:113:5
+  --> $DIR/no_effect.rs:112:5
    |
 LL |     5..=6;
    |     ^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:114:5
+  --> $DIR/no_effect.rs:113:5
    |
 LL |     [42, 55];
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:115:5
+  --> $DIR/no_effect.rs:114:5
    |
 LL |     [42, 55][1];
    |     ^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:116:5
+  --> $DIR/no_effect.rs:115:5
    |
 LL |     (42, 55).1;
    |     ^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:117:5
+  --> $DIR/no_effect.rs:116:5
    |
 LL |     [42; 55];
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:118:5
+  --> $DIR/no_effect.rs:117:5
    |
 LL |     [42; 55][13];
    |     ^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:120:5
+  --> $DIR/no_effect.rs:119:5
    |
 LL |     || x += 5;
    |     ^^^^^^^^^^
 
 error: statement with no effect
-  --> $DIR/no_effect.rs:122:5
+  --> $DIR/no_effect.rs:121:5
    |
 LL |     FooString { s: s };
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:123:5
+  --> $DIR/no_effect.rs:122:5
    |
 LL |     let _unused = 1;
    |     ^^^^^^^^^^^^^^^^
@@ -165,19 +165,19 @@ LL |     let _unused = 1;
    = note: `-D clippy::no-effect-underscore-binding` implied by `-D warnings`
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:124:5
+  --> $DIR/no_effect.rs:123:5
    |
 LL |     let _penguin = || println!("Some helpful closure");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:125:5
+  --> $DIR/no_effect.rs:124:5
    |
 LL |     let _duck = Struct { field: 0 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> $DIR/no_effect.rs:126:5
+  --> $DIR/no_effect.rs:125:5
    |
 LL |     let _cat = [2, 4, 6, 8][2];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
index 123aed40251e2a639a63a6b630fc6a084944e02c..fdb08d953ff1dbc95263e6d41e88861310d9a6b5 100644 (file)
@@ -185,7 +185,8 @@ mod issue8239 {
             .reduce(|mut acc, f| {
                 acc.push_str(&f);
                 acc
-            }).unwrap_or_default();
+            })
+            .unwrap_or_default();
     }
 
     fn more_to_max_suggestion_highest_lines_1() {
@@ -197,7 +198,8 @@ mod issue8239 {
                 let _ = "";
                 acc.push_str(&f);
                 acc
-            }).unwrap_or_default();
+            })
+            .unwrap_or_default();
     }
 
     fn equal_to_max_suggestion_highest_lines() {
@@ -208,7 +210,8 @@ mod issue8239 {
                 let _ = "";
                 acc.push_str(&f);
                 acc
-            }).unwrap_or_default();
+            })
+            .unwrap_or_default();
     }
 
     fn less_than_max_suggestion_highest_lines() {
@@ -218,7 +221,8 @@ mod issue8239 {
         map.reduce(|mut acc, f| {
             acc.push_str(&f);
             acc
-        }).unwrap_or_default();
+        })
+        .unwrap_or_default();
     }
 }
 
index dfe15654bc32c05db6992bfde425b6ba40915883..4c5938ab88b90926c25c189063ad1b4ab1e268f5 100644 (file)
@@ -7,10 +7,10 @@ LL |     with_constructor.unwrap_or(make());
    = note: `-D clippy::or-fun-call` implied by `-D warnings`
 
 error: use of `unwrap_or` followed by a call to `new`
-  --> $DIR/or_fun_call.rs:52:5
+  --> $DIR/or_fun_call.rs:52:14
    |
 LL |     with_new.unwrap_or(Vec::new());
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_new.unwrap_or_default()`
+   |              ^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 
 error: use of `unwrap_or` followed by a function call
   --> $DIR/or_fun_call.rs:55:21
@@ -31,16 +31,16 @@ LL |     with_err_args.unwrap_or(Vec::with_capacity(12));
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|_| Vec::with_capacity(12))`
 
 error: use of `unwrap_or` followed by a call to `default`
-  --> $DIR/or_fun_call.rs:64:5
+  --> $DIR/or_fun_call.rs:64:24
    |
 LL |     with_default_trait.unwrap_or(Default::default());
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_trait.unwrap_or_default()`
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 
 error: use of `unwrap_or` followed by a call to `default`
-  --> $DIR/or_fun_call.rs:67:5
+  --> $DIR/or_fun_call.rs:67:23
    |
 LL |     with_default_type.unwrap_or(u64::default());
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_default_type.unwrap_or_default()`
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 
 error: use of `unwrap_or` followed by a function call
   --> $DIR/or_fun_call.rs:70:18
@@ -49,16 +49,16 @@ LL |     self_default.unwrap_or(<FakeDefault>::default());
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(<FakeDefault>::default)`
 
 error: use of `unwrap_or` followed by a call to `default`
-  --> $DIR/or_fun_call.rs:73:5
+  --> $DIR/or_fun_call.rs:73:18
    |
 LL |     real_default.unwrap_or(<FakeDefault as Default>::default());
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `real_default.unwrap_or_default()`
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 
 error: use of `unwrap_or` followed by a call to `new`
-  --> $DIR/or_fun_call.rs:76:5
+  --> $DIR/or_fun_call.rs:76:14
    |
 LL |     with_vec.unwrap_or(vec![]);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `with_vec.unwrap_or_default()`
+   |              ^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 
 error: use of `unwrap_or` followed by a function call
   --> $DIR/or_fun_call.rs:79:21
@@ -109,90 +109,28 @@ LL |         None.unwrap_or( unsafe { ptr_to_ref(s) }    );
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
 
 error: use of `unwrap_or` followed by a call to `new`
-  --> $DIR/or_fun_call.rs:182:9
-   |
-LL | /         frames
-LL | |             .iter()
-LL | |             .map(|f: &String| f.to_lowercase())
-LL | |             .reduce(|mut acc, f| {
-...  |
-LL | |             })
-LL | |             .unwrap_or(String::new());
-   | |_____________________________________^
-   |
-help: try this
-   |
-LL ~         frames
-LL +             .iter()
-LL +             .map(|f: &String| f.to_lowercase())
-LL +             .reduce(|mut acc, f| {
-LL +                 acc.push_str(&f);
-LL +                 acc
-LL ~             }).unwrap_or_default();
+  --> $DIR/or_fun_call.rs:189:14
    |
+LL |             .unwrap_or(String::new());
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 
 error: use of `unwrap_or` followed by a call to `new`
-  --> $DIR/or_fun_call.rs:195:9
-   |
-LL | /         iter.map(|f: &String| f.to_lowercase())
-LL | |             .reduce(|mut acc, f| {
-LL | |                 let _ = "";
-LL | |                 let _ = "";
-...  |
-LL | |             })
-LL | |             .unwrap_or(String::new());
-   | |_____________________________________^
-   |
-help: try this
-   |
-LL ~         iter.map(|f: &String| f.to_lowercase())
-LL +             .reduce(|mut acc, f| {
-LL +                 let _ = "";
-LL +                 let _ = "";
-LL +                 acc.push_str(&f);
-LL +                 acc
-LL ~             }).unwrap_or_default();
+  --> $DIR/or_fun_call.rs:202:14
    |
+LL |             .unwrap_or(String::new());
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 
 error: use of `unwrap_or` followed by a call to `new`
-  --> $DIR/or_fun_call.rs:208:9
-   |
-LL | /         iter.map(|f: &String| f.to_lowercase())
-LL | |             .reduce(|mut acc, f| {
-LL | |                 let _ = "";
-LL | |                 acc.push_str(&f);
-LL | |                 acc
-LL | |             })
-LL | |             .unwrap_or(String::new());
-   | |_____________________________________^
-   |
-help: try this
-   |
-LL ~         iter.map(|f: &String| f.to_lowercase())
-LL +             .reduce(|mut acc, f| {
-LL +                 let _ = "";
-LL +                 acc.push_str(&f);
-LL +                 acc
-LL ~             }).unwrap_or_default();
+  --> $DIR/or_fun_call.rs:214:14
    |
+LL |             .unwrap_or(String::new());
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 
 error: use of `unwrap_or` followed by a call to `new`
-  --> $DIR/or_fun_call.rs:221:9
-   |
-LL | /         map.reduce(|mut acc, f| {
-LL | |             acc.push_str(&f);
-LL | |             acc
-LL | |         })
-LL | |         .unwrap_or(String::new());
-   | |_________________________________^
-   |
-help: try this
-   |
-LL ~         map.reduce(|mut acc, f| {
-LL +             acc.push_str(&f);
-LL +             acc
-LL ~         }).unwrap_or_default();
+  --> $DIR/or_fun_call.rs:225:10
    |
+LL |         .unwrap_or(String::new());
+   |          ^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_default()`
 
 error: aborting due to 22 previous errors
 
index 13ce0f32d4bb14b7e6f2777be15ee32fc6e7e52b..c4c9c82143336647e2dafb04292544c81797c6d5 100644 (file)
@@ -1,5 +1,6 @@
 // run-rustfix
 #![allow(unreachable_code)]
+#![allow(dead_code)]
 #![allow(clippy::unnecessary_wraps)]
 
 fn some_func(a: Option<u32>) -> Option<u32> {
@@ -154,26 +155,56 @@ fn f() -> NotOption {
     NotOption::First
 }
 
-fn main() {
-    some_func(Some(42));
-    some_func(None);
-    some_other_func(Some(42));
+fn do_something() {}
 
-    let copy_struct = CopyStruct { opt: Some(54) };
-    copy_struct.func();
+fn err_immediate_return() -> Result<i32, i32> {
+    func_returning_result()?;
+    Ok(1)
+}
 
-    let move_struct = MoveStruct {
-        opt: Some(vec![42, 1337]),
-    };
-    move_struct.ref_func();
-    move_struct.clone().mov_func_reuse();
-    move_struct.mov_func_no_use();
+fn err_immediate_return_and_do_something() -> Result<i32, i32> {
+    func_returning_result()?;
+    do_something();
+    Ok(1)
+}
 
-    let so = SeemsOption::Some(45);
-    returns_something_similar_to_option(so);
+// No warning
+fn no_immediate_return() -> Result<i32, i32> {
+    if let Err(err) = func_returning_result() {
+        do_something();
+        return Err(err);
+    }
+    Ok(1)
+}
 
-    func();
+// No warning
+fn mixed_result_and_option() -> Option<i32> {
+    if let Err(err) = func_returning_result() {
+        return Some(err);
+    }
+    None
+}
+
+// No warning
+fn else_if_check() -> Result<i32, i32> {
+    if true {
+        Ok(1)
+    } else if let Err(e) = func_returning_result() {
+        Err(e)
+    } else {
+        Err(-1)
+    }
+}
 
-    let _ = result_func(Ok(42));
-    let _ = f();
+// No warning
+#[allow(clippy::manual_map)]
+#[rustfmt::skip]
+fn option_map() -> Option<bool> {
+    if let Some(a) = Some(false) {
+        Some(!a)
+    } else {
+        None
+    }
 }
+
+fn main() {}
index 60590fd931188042efee4d99058ea3dac6ecd219..cdbc7b1606f80782c7d107e213311e4b79cb6769 100644 (file)
@@ -1,5 +1,6 @@
 // run-rustfix
 #![allow(unreachable_code)]
+#![allow(dead_code)]
 #![allow(clippy::unnecessary_wraps)]
 
 fn some_func(a: Option<u32>) -> Option<u32> {
@@ -186,26 +187,60 @@ fn f() -> NotOption {
     NotOption::First
 }
 
-fn main() {
-    some_func(Some(42));
-    some_func(None);
-    some_other_func(Some(42));
+fn do_something() {}
 
-    let copy_struct = CopyStruct { opt: Some(54) };
-    copy_struct.func();
+fn err_immediate_return() -> Result<i32, i32> {
+    if let Err(err) = func_returning_result() {
+        return Err(err);
+    }
+    Ok(1)
+}
 
-    let move_struct = MoveStruct {
-        opt: Some(vec![42, 1337]),
-    };
-    move_struct.ref_func();
-    move_struct.clone().mov_func_reuse();
-    move_struct.mov_func_no_use();
+fn err_immediate_return_and_do_something() -> Result<i32, i32> {
+    if let Err(err) = func_returning_result() {
+        return Err(err);
+    }
+    do_something();
+    Ok(1)
+}
 
-    let so = SeemsOption::Some(45);
-    returns_something_similar_to_option(so);
+// No warning
+fn no_immediate_return() -> Result<i32, i32> {
+    if let Err(err) = func_returning_result() {
+        do_something();
+        return Err(err);
+    }
+    Ok(1)
+}
 
-    func();
+// No warning
+fn mixed_result_and_option() -> Option<i32> {
+    if let Err(err) = func_returning_result() {
+        return Some(err);
+    }
+    None
+}
 
-    let _ = result_func(Ok(42));
-    let _ = f();
+// No warning
+fn else_if_check() -> Result<i32, i32> {
+    if true {
+        Ok(1)
+    } else if let Err(e) = func_returning_result() {
+        Err(e)
+    } else {
+        Err(-1)
+    }
 }
+
+// No warning
+#[allow(clippy::manual_map)]
+#[rustfmt::skip]
+fn option_map() -> Option<bool> {
+    if let Some(a) = Some(false) {
+        Some(!a)
+    } else {
+        None
+    }
+}
+
+fn main() {}
index 8d782b71dd6a47ae8368b0d8f8c7ba89b13b34b6..1b6cd524b2f2339484878443a9e09f2acc00917a 100644 (file)
@@ -1,5 +1,5 @@
 error: this block may be rewritten with the `?` operator
-  --> $DIR/question_mark.rs:6:5
+  --> $DIR/question_mark.rs:7:5
    |
 LL | /     if a.is_none() {
 LL | |         return None;
@@ -9,7 +9,7 @@ LL | |     }
    = note: `-D clippy::question-mark` implied by `-D warnings`
 
 error: this block may be rewritten with the `?` operator
-  --> $DIR/question_mark.rs:51:9
+  --> $DIR/question_mark.rs:52:9
    |
 LL | /         if (self.opt).is_none() {
 LL | |             return None;
@@ -17,7 +17,7 @@ LL | |         }
    | |_________^ help: replace it with: `(self.opt)?;`
 
 error: this block may be rewritten with the `?` operator
-  --> $DIR/question_mark.rs:55:9
+  --> $DIR/question_mark.rs:56:9
    |
 LL | /         if self.opt.is_none() {
 LL | |             return None
@@ -25,7 +25,7 @@ LL | |         }
    | |_________^ help: replace it with: `self.opt?;`
 
 error: this block may be rewritten with the `?` operator
-  --> $DIR/question_mark.rs:59:17
+  --> $DIR/question_mark.rs:60:17
    |
 LL |           let _ = if self.opt.is_none() {
    |  _________________^
@@ -35,8 +35,8 @@ LL | |             self.opt
 LL | |         };
    | |_________^ help: replace it with: `Some(self.opt?)`
 
-error: this if-let-else may be rewritten with the `?` operator
-  --> $DIR/question_mark.rs:65:17
+error: this block may be rewritten with the `?` operator
+  --> $DIR/question_mark.rs:66:17
    |
 LL |           let _ = if let Some(x) = self.opt {
    |  _________________^
@@ -47,7 +47,7 @@ LL | |         };
    | |_________^ help: replace it with: `self.opt?`
 
 error: this block may be rewritten with the `?` operator
-  --> $DIR/question_mark.rs:82:9
+  --> $DIR/question_mark.rs:83:9
    |
 LL | /         if self.opt.is_none() {
 LL | |             return None;
@@ -55,7 +55,7 @@ LL | |         }
    | |_________^ help: replace it with: `self.opt.as_ref()?;`
 
 error: this block may be rewritten with the `?` operator
-  --> $DIR/question_mark.rs:90:9
+  --> $DIR/question_mark.rs:91:9
    |
 LL | /         if self.opt.is_none() {
 LL | |             return None;
@@ -63,15 +63,15 @@ LL | |         }
    | |_________^ help: replace it with: `self.opt.as_ref()?;`
 
 error: this block may be rewritten with the `?` operator
-  --> $DIR/question_mark.rs:98:9
+  --> $DIR/question_mark.rs:99:9
    |
 LL | /         if self.opt.is_none() {
 LL | |             return None;
 LL | |         }
    | |_________^ help: replace it with: `self.opt.as_ref()?;`
 
-error: this if-let-else may be rewritten with the `?` operator
-  --> $DIR/question_mark.rs:105:26
+error: this block may be rewritten with the `?` operator
+  --> $DIR/question_mark.rs:106:26
    |
 LL |           let v: &Vec<_> = if let Some(ref v) = self.opt {
    |  __________________________^
@@ -81,8 +81,8 @@ LL | |             return None;
 LL | |         };
    | |_________^ help: replace it with: `self.opt.as_ref()?`
 
-error: this if-let-else may be rewritten with the `?` operator
-  --> $DIR/question_mark.rs:115:17
+error: this block may be rewritten with the `?` operator
+  --> $DIR/question_mark.rs:116:17
    |
 LL |           let v = if let Some(v) = self.opt {
    |  _________________^
@@ -93,26 +93,42 @@ LL | |         };
    | |_________^ help: replace it with: `self.opt?`
 
 error: this block may be rewritten with the `?` operator
-  --> $DIR/question_mark.rs:130:5
+  --> $DIR/question_mark.rs:131:5
    |
 LL | /     if f().is_none() {
 LL | |         return None;
 LL | |     }
    | |_____^ help: replace it with: `f()?;`
 
-error: this if-let-else may be rewritten with the `?` operator
-  --> $DIR/question_mark.rs:142:13
+error: this block may be rewritten with the `?` operator
+  --> $DIR/question_mark.rs:143:13
    |
 LL |     let _ = if let Ok(x) = x { x } else { return x };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?`
 
 error: this block may be rewritten with the `?` operator
-  --> $DIR/question_mark.rs:144:5
+  --> $DIR/question_mark.rs:145:5
    |
 LL | /     if x.is_err() {
 LL | |         return x;
 LL | |     }
    | |_____^ help: replace it with: `x?;`
 
-error: aborting due to 13 previous errors
+error: this block may be rewritten with the `?` operator
+  --> $DIR/question_mark.rs:193:5
+   |
+LL | /     if let Err(err) = func_returning_result() {
+LL | |         return Err(err);
+LL | |     }
+   | |_____^ help: replace it with: `func_returning_result()?;`
+
+error: this block may be rewritten with the `?` operator
+  --> $DIR/question_mark.rs:200:5
+   |
+LL | /     if let Err(err) = func_returning_result() {
+LL | |         return Err(err);
+LL | |     }
+   | |_____^ help: replace it with: `func_returning_result()?;`
+
+error: aborting due to 15 previous errors
 
index 9562b47f0c4ff28610caf5f78856225725642d77..daef95a425c92b564e6a202bca3be4b3d5a80193 100644 (file)
@@ -62,7 +62,7 @@ fn foo() {}
         impl T1 for S {}
     }
 
-    mod mulitply_conflicit_trait {
+    mod multiply_conflicit_trait {
         use crate::{T1, T2};
 
         struct S;
index 5a2aee465d1bdee6d95cd79f1763aef9b73e4300..385a9986aba047bfdfe30cf1fc3818e340eca0ab 100644 (file)
@@ -216,3 +216,33 @@ mod issue7392 {
         let _ = v.iter().any(|fp| test_u32_2(*fp.field));
     }
 }
+
+mod issue9120 {
+    fn make_arg_no_deref_impl() -> impl Fn(&&u32) -> bool {
+        move |x: &&u32| **x == 78
+    }
+
+    fn make_arg_no_deref_dyn() -> Box<dyn Fn(&&u32) -> bool> {
+        Box::new(move |x: &&u32| **x == 78)
+    }
+
+    fn wrapper<T: Fn(&&u32) -> bool>(v: Vec<u32>, func: T) -> bool {
+        #[allow(clippy::redundant_closure)]
+        v.iter().any(|x: &u32| func(&x))
+    }
+
+    fn do_tests() {
+        let v = vec![3, 2, 1, 0];
+        let arg_no_deref_impl = make_arg_no_deref_impl();
+        let arg_no_deref_dyn = make_arg_no_deref_dyn();
+
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().any(|x: &u32| arg_no_deref_impl(&x));
+
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().any(|x: &u32| arg_no_deref_dyn(&x));
+
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().any(|x: &u32| (*arg_no_deref_dyn)(&x));
+    }
+}
index 0e98ae18a217cb840dad5c974c1b221488c64b14..67e190ee37837d471b9c7eb7f5d05b859a04dafd 100644 (file)
@@ -219,3 +219,33 @@ struct FieldProjection<'a> {
         let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
     }
 }
+
+mod issue9120 {
+    fn make_arg_no_deref_impl() -> impl Fn(&&u32) -> bool {
+        move |x: &&u32| **x == 78
+    }
+
+    fn make_arg_no_deref_dyn() -> Box<dyn Fn(&&u32) -> bool> {
+        Box::new(move |x: &&u32| **x == 78)
+    }
+
+    fn wrapper<T: Fn(&&u32) -> bool>(v: Vec<u32>, func: T) -> bool {
+        #[allow(clippy::redundant_closure)]
+        v.iter().find(|x: &&u32| func(x)).is_some()
+    }
+
+    fn do_tests() {
+        let v = vec![3, 2, 1, 0];
+        let arg_no_deref_impl = make_arg_no_deref_impl();
+        let arg_no_deref_dyn = make_arg_no_deref_dyn();
+
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().find(|x: &&u32| arg_no_deref_impl(x)).is_some();
+
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().find(|x: &&u32| arg_no_deref_dyn(x)).is_some();
+
+        #[allow(clippy::redundant_closure)]
+        let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some();
+    }
+}
index 8b424f18ef5b50c60f10d3c69837d748a0778ff6..c5c3c92c9182fc32aa3de5c6f9bdda18dee8d126 100644 (file)
@@ -264,5 +264,29 @@ error: called `is_some()` after searching an `Iterator` with `find`
 LL |         let _ = v.iter().find(|fp| test_u32_2(*fp.field)).is_some();
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|fp| test_u32_2(*fp.field))`
 
-error: aborting due to 43 previous errors
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:234:18
+   |
+LL |         v.iter().find(|x: &&u32| func(x)).is_some()
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| func(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:243:26
+   |
+LL |         let _ = v.iter().find(|x: &&u32| arg_no_deref_impl(x)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref_impl(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:246:26
+   |
+LL |         let _ = v.iter().find(|x: &&u32| arg_no_deref_dyn(x)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| arg_no_deref_dyn(&x))`
+
+error: called `is_some()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable_some.rs:249:26
+   |
+LL |         let _ = v.iter().find(|x: &&u32| (*arg_no_deref_dyn)(x)).is_some();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `any()` instead: `any(|x: &u32| (*arg_no_deref_dyn)(&x))`
+
+error: aborting due to 47 previous errors
 
index 3bd41d0626049da49e07ce5c0f50ce10c6f2c0d4..43d76094d0e847e3e6f60c875d2cf2248adc330b 100644 (file)
@@ -265,5 +265,17 @@ note: previous binding is here
 LL | pub async fn foo2(_a: i32, _b: i64) {
    |                            ^^
 
-error: aborting due to 22 previous errors
+error: `x` shadows a previous, unrelated binding
+  --> $DIR/shadow.rs:94:21
+   |
+LL |         if let Some(x) = Some(1) { x } else { 1 }
+   |                     ^
+   |
+note: previous binding is here
+  --> $DIR/shadow.rs:93:13
+   |
+LL |         let x = 1;
+   |             ^
+
+error: aborting due to 23 previous errors
 
index 185e5009b60f1500db71b3e49cbad47f3cc09462..84ecf1ea53ed73207e78abb145bee2ae7b8a47f3 100644 (file)
@@ -620,4 +620,11 @@ fn should_trigger_lint_without_significant_drop_in_arm() {
     };
 }
 
+fn should_not_trigger_on_significant_iterator_drop() {
+    let lines = std::io::stdin().lines();
+    for line in lines {
+        println!("foo: {}", line.unwrap());
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs
new file mode 100644 (file)
index 0000000..74f05ec
--- /dev/null
@@ -0,0 +1,39 @@
+#![warn(clippy::std_instead_of_core)]
+#![allow(unused_imports)]
+
+extern crate alloc;
+
+#[warn(clippy::std_instead_of_core)]
+fn std_instead_of_core() {
+    // Regular import
+    use std::hash::Hasher;
+    // Absolute path
+    use ::std::hash::Hash;
+
+    // Multiple imports
+    use std::fmt::{Debug, Result};
+
+    // Function calls
+    let ptr = std::ptr::null::<u32>();
+    let ptr_mut = ::std::ptr::null_mut::<usize>();
+
+    // Types
+    let cell = std::cell::Cell::new(8u32);
+    let cell_absolute = ::std::cell::Cell::new(8u32);
+}
+
+#[warn(clippy::std_instead_of_alloc)]
+fn std_instead_of_alloc() {
+    use std::vec::Vec;
+}
+
+#[warn(clippy::alloc_instead_of_core)]
+fn alloc_instead_of_core() {
+    use alloc::slice::from_ref;
+}
+
+fn main() {
+    std_instead_of_core();
+    std_instead_of_alloc();
+    alloc_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
new file mode 100644 (file)
index 0000000..9f16448
--- /dev/null
@@ -0,0 +1,85 @@
+error: used import from `std` instead of `core`
+  --> $DIR/std_instead_of_core.rs:9:9
+   |
+LL |     use std::hash::Hasher;
+   |         ^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::std-instead-of-core` implied by `-D warnings`
+   = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+  --> $DIR/std_instead_of_core.rs:11:9
+   |
+LL |     use ::std::hash::Hash;
+   |         ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+  --> $DIR/std_instead_of_core.rs:14:20
+   |
+LL |     use std::fmt::{Debug, Result};
+   |                    ^^^^^
+   |
+   = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+  --> $DIR/std_instead_of_core.rs:14:27
+   |
+LL |     use std::fmt::{Debug, Result};
+   |                           ^^^^^^
+   |
+   = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+  --> $DIR/std_instead_of_core.rs:17:15
+   |
+LL |     let ptr = std::ptr::null::<u32>();
+   |               ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+  --> $DIR/std_instead_of_core.rs:18:19
+   |
+LL |     let ptr_mut = ::std::ptr::null_mut::<usize>();
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+  --> $DIR/std_instead_of_core.rs:21:16
+   |
+LL |     let cell = std::cell::Cell::new(8u32);
+   |                ^^^^^^^^^^^^^^^
+   |
+   = help: consider importing the item from `core`
+
+error: used import from `std` instead of `core`
+  --> $DIR/std_instead_of_core.rs:22:25
+   |
+LL |     let cell_absolute = ::std::cell::Cell::new(8u32);
+   |                         ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider importing the item from `core`
+
+error: used import from `std` instead of `alloc`
+  --> $DIR/std_instead_of_core.rs:27:9
+   |
+LL |     use std::vec::Vec;
+   |         ^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings`
+   = help: consider importing the item from `alloc`
+
+error: used import from `alloc` instead of `core`
+  --> $DIR/std_instead_of_core.rs:32:9
+   |
+LL |     use alloc::slice::from_ref;
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings`
+   = help: consider importing the item from `core`
+
+error: aborting due to 10 previous errors
+
index a21d4c5d637daec8678ce052656d1d86bff631aa..a5751c58aab8f09f1b7c032f281d4281e001e2d5 100644 (file)
@@ -1,4 +1,5 @@
 #![deny(clippy::trait_duplication_in_bounds)]
+#![allow(unused)]
 
 use std::collections::BTreeMap;
 use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
@@ -98,4 +99,114 @@ fn bar()
 // This should not lint
 fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
 
+mod repeated_where_clauses_or_trait_bounds {
+    fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
+        unimplemented!();
+    }
+
+    fn bad_bar<T, U>(arg0: T, arg1: U)
+    where
+        T: Clone + Clone + Clone + Copy,
+        U: Clone + Copy,
+    {
+        unimplemented!();
+    }
+
+    fn good_bar<T: Clone + Copy, U: Clone + Copy>(arg0: T, arg1: U) {
+        unimplemented!();
+    }
+
+    fn good_foo<T, U>(arg0: T, arg1: U)
+    where
+        T: Clone + Copy,
+        U: Clone + Copy,
+    {
+        unimplemented!();
+    }
+
+    trait GoodSelfTraitBound: Clone + Copy {
+        fn f();
+    }
+
+    trait GoodSelfWhereClause {
+        fn f()
+        where
+            Self: Clone + Copy;
+    }
+
+    trait BadSelfTraitBound: Clone + Clone + Clone {
+        fn f();
+    }
+
+    trait BadSelfWhereClause {
+        fn f()
+        where
+            Self: Clone + Clone + Clone;
+    }
+
+    trait GoodTraitBound<T: Clone + Copy, U: Clone + Copy> {
+        fn f();
+    }
+
+    trait GoodWhereClause<T, U> {
+        fn f()
+        where
+            T: Clone + Copy,
+            U: Clone + Copy;
+    }
+
+    trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
+        fn f();
+    }
+
+    trait BadWhereClause<T, U> {
+        fn f()
+        where
+            T: Clone + Clone + Clone + Copy,
+            U: Clone + Copy;
+    }
+
+    struct GoodStructBound<T: Clone + Copy, U: Clone + Copy> {
+        t: T,
+        u: U,
+    }
+
+    impl<T: Clone + Copy, U: Clone + Copy> GoodTraitBound<T, U> for GoodStructBound<T, U> {
+        // this should not warn
+        fn f() {}
+    }
+
+    struct GoodStructWhereClause;
+
+    impl<T, U> GoodTraitBound<T, U> for GoodStructWhereClause
+    where
+        T: Clone + Copy,
+        U: Clone + Copy,
+    {
+        // this should not warn
+        fn f() {}
+    }
+
+    fn no_error_separate_arg_bounds(program: impl AsRef<()>, dir: impl AsRef<()>, args: &[impl AsRef<()>]) {}
+
+    trait GenericTrait<T> {}
+
+    // This should not warn but currently does see #8757
+    fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
+        unimplemented!();
+    }
+
+    fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
+        unimplemented!();
+    }
+
+    mod foo {
+        pub trait Clone {}
+    }
+
+    fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
+        unimplemented!();
+    }
+}
+
 fn main() {}
index 6f8c8e47dfbf1fe56589a06f80017513e69167e8..7ef04e52708f4c91e16c67c6dddf6cc786779c9b 100644 (file)
@@ -1,5 +1,5 @@
 error: this trait bound is already specified in the where clause
-  --> $DIR/trait_duplication_in_bounds.rs:6:15
+  --> $DIR/trait_duplication_in_bounds.rs:7:15
    |
 LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
    |               ^^^^^
@@ -12,7 +12,7 @@ LL | #![deny(clippy::trait_duplication_in_bounds)]
    = help: consider removing this trait bound
 
 error: this trait bound is already specified in the where clause
-  --> $DIR/trait_duplication_in_bounds.rs:6:23
+  --> $DIR/trait_duplication_in_bounds.rs:7:23
    |
 LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
    |                       ^^^^^^^
@@ -20,7 +20,7 @@ LL | fn bad_foo<T: Clone + Default, Z: Copy>(arg0: T, arg1: Z)
    = help: consider removing this trait bound
 
 error: this trait bound is already specified in trait declaration
-  --> $DIR/trait_duplication_in_bounds.rs:35:15
+  --> $DIR/trait_duplication_in_bounds.rs:36:15
    |
 LL |         Self: Default;
    |               ^^^^^^^
@@ -28,7 +28,7 @@ LL |         Self: Default;
    = help: consider removing this trait bound
 
 error: this trait bound is already specified in trait declaration
-  --> $DIR/trait_duplication_in_bounds.rs:49:15
+  --> $DIR/trait_duplication_in_bounds.rs:50:15
    |
 LL |         Self: Default + Clone;
    |               ^^^^^^^
@@ -36,7 +36,7 @@ LL |         Self: Default + Clone;
    = help: consider removing this trait bound
 
 error: this trait bound is already specified in trait declaration
-  --> $DIR/trait_duplication_in_bounds.rs:55:15
+  --> $DIR/trait_duplication_in_bounds.rs:56:15
    |
 LL |         Self: Default + Clone;
    |               ^^^^^^^
@@ -44,7 +44,7 @@ LL |         Self: Default + Clone;
    = help: consider removing this trait bound
 
 error: this trait bound is already specified in trait declaration
-  --> $DIR/trait_duplication_in_bounds.rs:55:25
+  --> $DIR/trait_duplication_in_bounds.rs:56:25
    |
 LL |         Self: Default + Clone;
    |                         ^^^^^
@@ -52,7 +52,7 @@ LL |         Self: Default + Clone;
    = help: consider removing this trait bound
 
 error: this trait bound is already specified in trait declaration
-  --> $DIR/trait_duplication_in_bounds.rs:58:15
+  --> $DIR/trait_duplication_in_bounds.rs:59:15
    |
 LL |         Self: Default;
    |               ^^^^^^^
@@ -60,12 +60,108 @@ LL |         Self: Default;
    = help: consider removing this trait bound
 
 error: this trait bound is already specified in trait declaration
-  --> $DIR/trait_duplication_in_bounds.rs:93:15
+  --> $DIR/trait_duplication_in_bounds.rs:94:15
    |
 LL |         Self: Iterator<Item = Foo>,
    |               ^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider removing this trait bound
 
-error: aborting due to 8 previous errors
+error: this trait bound is already specified in the where clause
+  --> $DIR/trait_duplication_in_bounds.rs:103:19
+   |
+LL |     fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
+   |                   ^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: these bounds contain repeated elements
+  --> $DIR/trait_duplication_in_bounds.rs:103:19
+   |
+LL |     fn bad_foo<T: Clone + Clone + Clone + Copy, U: Clone + Copy>(arg0: T, argo1: U) {
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
+
+error: this trait bound is already specified in the where clause
+  --> $DIR/trait_duplication_in_bounds.rs:109:12
+   |
+LL |         T: Clone + Clone + Clone + Copy,
+   |            ^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: these where clauses contain repeated elements
+  --> $DIR/trait_duplication_in_bounds.rs:109:12
+   |
+LL |         T: Clone + Clone + Clone + Copy,
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
+
+error: these bounds contain repeated elements
+  --> $DIR/trait_duplication_in_bounds.rs:137:30
+   |
+LL |     trait BadSelfTraitBound: Clone + Clone + Clone {
+   |                              ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
+
+error: these where clauses contain repeated elements
+  --> $DIR/trait_duplication_in_bounds.rs:144:19
+   |
+LL |             Self: Clone + Clone + Clone;
+   |                   ^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone`
+
+error: this trait bound is already specified in the where clause
+  --> $DIR/trait_duplication_in_bounds.rs:158:28
+   |
+LL |     trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
+   |                            ^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: these bounds contain repeated elements
+  --> $DIR/trait_duplication_in_bounds.rs:158:28
+   |
+LL |     trait BadTraitBound<T: Clone + Clone + Clone + Copy, U: Clone + Copy> {
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
+
+error: these where clauses contain repeated elements
+  --> $DIR/trait_duplication_in_bounds.rs:165:16
+   |
+LL |             T: Clone + Clone + Clone + Copy,
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + Copy`
+
+error: this trait bound is already specified in the where clause
+  --> $DIR/trait_duplication_in_bounds.rs:195:24
+   |
+LL |     fn good_generic<T: GenericTrait<u64> + GenericTrait<u32>>(arg0: T) {
+   |                        ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: this trait bound is already specified in the where clause
+  --> $DIR/trait_duplication_in_bounds.rs:199:23
+   |
+LL |     fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
+   |                       ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: these bounds contain repeated elements
+  --> $DIR/trait_duplication_in_bounds.rs:199:23
+   |
+LL |     fn bad_generic<T: GenericTrait<u64> + GenericTrait<u32> + GenericTrait<u64>>(arg0: T) {
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `GenericTrait<u32> + GenericTrait<u64>`
+
+error: this trait bound is already specified in the where clause
+  --> $DIR/trait_duplication_in_bounds.rs:207:26
+   |
+LL |     fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
+   |                          ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider removing this trait bound
+
+error: these bounds contain repeated elements
+  --> $DIR/trait_duplication_in_bounds.rs:207:26
+   |
+LL |     fn qualified_path<T: std::clone::Clone + Clone + foo::Clone>(arg0: T) {
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Clone + foo::Clone`
+
+error: aborting due to 22 previous errors
 
index b425cdd6cbfd441d45b3cacce9a3f2c60daa5b7e..539239fc18f9449f8b582b68a930f39ae2fcf664 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 #![warn(clippy::transmutes_expressible_as_ptr_casts)]
-// These two warnings currrently cover the cases transmutes_expressible_as_ptr_casts
+// These two warnings currently cover the cases transmutes_expressible_as_ptr_casts
 // would otherwise be responsible for
 #![warn(clippy::useless_transmute)]
 #![warn(clippy::transmute_ptr_to_ptr)]
index 8fd57c5965279d96766ee8748195cca562277399..b9e446dc89a95f2160736ec83b80689642c4c0e5 100644 (file)
@@ -1,6 +1,6 @@
 // run-rustfix
 #![warn(clippy::transmutes_expressible_as_ptr_casts)]
-// These two warnings currrently cover the cases transmutes_expressible_as_ptr_casts
+// These two warnings currently cover the cases transmutes_expressible_as_ptr_casts
 // would otherwise be responsible for
 #![warn(clippy::useless_transmute)]
 #![warn(clippy::transmute_ptr_to_ptr)]
index d11432f9046111d4ff77f7279250bc7f10083975..2eca1f4701c9fdaeccb0da0160b67bea538f4da1 100644 (file)
@@ -79,6 +79,18 @@ struct Foo<T, U>
     u: U,
 }
 
+// Check for the `?` in `?Sized`
+pub fn f<T: ?Sized>()
+where
+    T: Clone,
+{
+}
+pub fn g<T: Clone>()
+where
+    T: ?Sized,
+{
+}
+
 // This should not lint
 fn impl_trait(_: impl AsRef<str>, _: impl AsRef<str>) {}
 
index 148c19c7d0701dc2910de718d175a03122d26e03..1d88714814d477007bc0dc6afc60f0fee9bb95a4 100644 (file)
@@ -19,5 +19,21 @@ LL |     Self: Copy + Default + Ord,
    |
    = help: consider combining the bounds: `Self: Clone + Copy + Default + Ord`
 
-error: aborting due to 2 previous errors
+error: this type has already been used as a bound predicate
+  --> $DIR/type_repetition_in_bounds.rs:85:5
+   |
+LL |     T: Clone,
+   |     ^^^^^^^^
+   |
+   = help: consider combining the bounds: `T: ?Sized + Clone`
+
+error: this type has already been used as a bound predicate
+  --> $DIR/type_repetition_in_bounds.rs:90:5
+   |
+LL |     T: ?Sized,
+   |     ^^^^^^^^^
+   |
+   = help: consider combining the bounds: `T: Clone + ?Sized`
+
+error: aborting due to 4 previous errors
 
index 33b6a82f9d2c32cefabdc09ca2c4ec6786039e4b..08aee4332151446a6fdd17603bd651cae7ee7306 100644 (file)
@@ -250,6 +250,11 @@ fn from_proc_macro() {
     proc_macro_unsafe::unsafe_block!(token);
 }
 
+fn in_closure(x: *const u32) {
+    // Safety: reason
+    let _ = || unsafe { *x };
+}
+
 // Invalid comments
 
 #[rustfmt::skip]
@@ -351,9 +356,9 @@ unsafe trait B {}
 
     #[rustfmt::skip]
     mod sub_mod2 {
-        // 
+        //
         // SAFETY: ok
-        // 
+        //
 
         unsafe impl B for (u32) {}
         unsafe trait B {}
index b79949e9d06d6af18a3602f980fe15f5fe30a6ec..c6a2127443befce4441fcc68018784fc0b362377 100644 (file)
@@ -1,5 +1,5 @@
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:257:19
+  --> $DIR/undocumented_unsafe_blocks.rs:262:19
    |
 LL |     /* Safety: */ unsafe {}
    |                   ^^^^^^^^^
@@ -8,7 +8,7 @@ LL |     /* Safety: */ unsafe {}
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:261:5
+  --> $DIR/undocumented_unsafe_blocks.rs:266:5
    |
 LL |     unsafe {}
    |     ^^^^^^^^^
@@ -16,7 +16,7 @@ LL |     unsafe {}
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:265:14
+  --> $DIR/undocumented_unsafe_blocks.rs:270:14
    |
 LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
    |              ^^^^^^^^^^^^^
@@ -24,7 +24,7 @@ LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:265:29
+  --> $DIR/undocumented_unsafe_blocks.rs:270:29
    |
 LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
    |                             ^^^^^^^^^^^^^
@@ -32,7 +32,7 @@ LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:265:48
+  --> $DIR/undocumented_unsafe_blocks.rs:270:48
    |
 LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
    |                                                ^^^^^^^^^^^^^
@@ -40,7 +40,7 @@ LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:269:18
+  --> $DIR/undocumented_unsafe_blocks.rs:274:18
    |
 LL |     let _ = (42, unsafe {}, "test", unsafe {});
    |                  ^^^^^^^^^
@@ -48,7 +48,7 @@ LL |     let _ = (42, unsafe {}, "test", unsafe {});
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:269:37
+  --> $DIR/undocumented_unsafe_blocks.rs:274:37
    |
 LL |     let _ = (42, unsafe {}, "test", unsafe {});
    |                                     ^^^^^^^^^
@@ -56,7 +56,7 @@ LL |     let _ = (42, unsafe {}, "test", unsafe {});
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:273:14
+  --> $DIR/undocumented_unsafe_blocks.rs:278:14
    |
 LL |     let _ = *unsafe { &42 };
    |              ^^^^^^^^^^^^^^
@@ -64,7 +64,7 @@ LL |     let _ = *unsafe { &42 };
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:278:19
+  --> $DIR/undocumented_unsafe_blocks.rs:283:19
    |
 LL |     let _ = match unsafe {} {
    |                   ^^^^^^^^^
@@ -72,7 +72,7 @@ LL |     let _ = match unsafe {} {
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:284:14
+  --> $DIR/undocumented_unsafe_blocks.rs:289:14
    |
 LL |     let _ = &unsafe {};
    |              ^^^^^^^^^
@@ -80,7 +80,7 @@ LL |     let _ = &unsafe {};
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:288:14
+  --> $DIR/undocumented_unsafe_blocks.rs:293:14
    |
 LL |     let _ = [unsafe {}; 5];
    |              ^^^^^^^^^
@@ -88,7 +88,7 @@ LL |     let _ = [unsafe {}; 5];
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:292:13
+  --> $DIR/undocumented_unsafe_blocks.rs:297:13
    |
 LL |     let _ = unsafe {};
    |             ^^^^^^^^^
@@ -96,7 +96,7 @@ LL |     let _ = unsafe {};
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:302:8
+  --> $DIR/undocumented_unsafe_blocks.rs:307:8
    |
 LL |     t!(unsafe {});
    |        ^^^^^^^^^
@@ -104,7 +104,7 @@ LL |     t!(unsafe {});
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:308:13
+  --> $DIR/undocumented_unsafe_blocks.rs:313:13
    |
 LL |             unsafe {}
    |             ^^^^^^^^^
@@ -116,7 +116,7 @@ LL |     t!();
    = note: this error originates in the macro `t` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:316:5
+  --> $DIR/undocumented_unsafe_blocks.rs:321:5
    |
 LL |     unsafe {} // SAFETY:
    |     ^^^^^^^^^
@@ -124,7 +124,7 @@ LL |     unsafe {} // SAFETY:
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:320:5
+  --> $DIR/undocumented_unsafe_blocks.rs:325:5
    |
 LL |     unsafe {
    |     ^^^^^^^^
@@ -132,7 +132,7 @@ LL |     unsafe {
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:330:5
+  --> $DIR/undocumented_unsafe_blocks.rs:335:5
    |
 LL |     unsafe {};
    |     ^^^^^^^^^
@@ -140,7 +140,7 @@ LL |     unsafe {};
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:334:20
+  --> $DIR/undocumented_unsafe_blocks.rs:339:20
    |
 LL |     println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -148,7 +148,7 @@ LL |     println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe impl missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:341:5
+  --> $DIR/undocumented_unsafe_blocks.rs:346:5
    |
 LL |     unsafe impl A for () {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^
@@ -156,7 +156,7 @@ LL |     unsafe impl A for () {}
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe impl missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:348:9
+  --> $DIR/undocumented_unsafe_blocks.rs:353:9
    |
 LL |         unsafe impl B for (u32) {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -164,7 +164,7 @@ LL |         unsafe impl B for (u32) {}
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe impl missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:369:13
+  --> $DIR/undocumented_unsafe_blocks.rs:374:13
    |
 LL |             unsafe impl T for $t {}
    |             ^^^^^^^^^^^^^^^^^^^^^^^
@@ -176,7 +176,7 @@ LL |     no_safety_comment!(());
    = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: unsafe impl missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:394:13
+  --> $DIR/undocumented_unsafe_blocks.rs:399:13
    |
 LL |             unsafe impl T for $t {}
    |             ^^^^^^^^^^^^^^^^^^^^^^^
@@ -188,7 +188,7 @@ LL |     no_safety_comment!(());
    = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: unsafe impl missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:402:5
+  --> $DIR/undocumented_unsafe_blocks.rs:407:5
    |
 LL |     unsafe impl T for (i32) {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -196,7 +196,7 @@ LL |     unsafe impl T for (i32) {}
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe impl missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:394:13
+  --> $DIR/undocumented_unsafe_blocks.rs:399:13
    |
 LL |             unsafe impl T for $t {}
    |             ^^^^^^^^^^^^^^^^^^^^^^^
@@ -208,7 +208,7 @@ LL |     no_safety_comment!(u32);
    = note: this error originates in the macro `no_safety_comment` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: unsafe impl missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:408:5
+  --> $DIR/undocumented_unsafe_blocks.rs:413:5
    |
 LL |     unsafe impl T for (bool) {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -216,7 +216,7 @@ LL |     unsafe impl T for (bool) {}
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe impl missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:454:5
+  --> $DIR/undocumented_unsafe_blocks.rs:459:5
    |
 LL |     unsafe impl NoComment for () {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -224,7 +224,7 @@ LL |     unsafe impl NoComment for () {}
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe impl missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:458:19
+  --> $DIR/undocumented_unsafe_blocks.rs:463:19
    |
 LL |     /* SAFETY: */ unsafe impl InlineComment for () {}
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -232,7 +232,7 @@ LL |     /* SAFETY: */ unsafe impl InlineComment for () {}
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe impl missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:462:5
+  --> $DIR/undocumented_unsafe_blocks.rs:467:5
    |
 LL |     unsafe impl TrailingComment for () {} // SAFETY:
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -240,7 +240,7 @@ LL |     unsafe impl TrailingComment for () {} // SAFETY:
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe impl missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:467:5
+  --> $DIR/undocumented_unsafe_blocks.rs:472:5
    |
 LL |     unsafe impl Interference for () {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -248,7 +248,7 @@ LL |     unsafe impl Interference for () {}
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe impl missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:474:5
+  --> $DIR/undocumented_unsafe_blocks.rs:479:5
    |
 LL |     unsafe impl ImplInFn for () {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -256,7 +256,7 @@ LL |     unsafe impl ImplInFn for () {}
    = help: consider adding a safety comment on the preceding line
 
 error: unsafe impl missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:483:1
+  --> $DIR/undocumented_unsafe_blocks.rs:488:1
    |
 LL | unsafe impl CrateRoot for () {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index 65fcdc43061bfe8557441050c3cc73c54121ccb5..eed817968832193aa2d7588043480134c3e04d13 100644 (file)
@@ -30,6 +30,7 @@ fn main() {
     let ext_opt = Some(42);
     let nested_opt = Some(Some(42));
     let nested_tuple_opt = Some(Some((42, 43)));
+    let cond = true;
 
     // Should lint - Option
     let _ = opt.unwrap_or(2);
@@ -42,6 +43,7 @@ fn main() {
     let _ = opt.get_or_insert(2);
     let _ = opt.ok_or(2);
     let _ = nested_tuple_opt.unwrap_or(Some((1, 2)));
+    let _ = cond.then_some(astronomers_pi);
 
     // Cases when unwrap is not called on a simple variable
     let _ = Some(10).unwrap_or(2);
index 206080ed69ada8dd7735ed9b6551cd7b12dc672b..1588db79b38a8c2345a5a623466a705998fedb97 100644 (file)
@@ -30,6 +30,7 @@ fn main() {
     let ext_opt = Some(42);
     let nested_opt = Some(Some(42));
     let nested_tuple_opt = Some(Some((42, 43)));
+    let cond = true;
 
     // Should lint - Option
     let _ = opt.unwrap_or_else(|| 2);
@@ -42,6 +43,7 @@ fn main() {
     let _ = opt.get_or_insert_with(|| 2);
     let _ = opt.ok_or_else(|| 2);
     let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
+    let _ = cond.then(|| astronomers_pi);
 
     // Cases when unwrap is not called on a simple variable
     let _ = Some(10).unwrap_or_else(|| 2);
index 7e4dd7730e71530b79aa78b6f41937a13c90b143..83dc7fd832c38f8a7974f6175fbb3ac91fd8a179 100644 (file)
@@ -1,5 +1,5 @@
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:35:13
+  --> $DIR/unnecessary_lazy_eval.rs:36:13
    |
 LL |     let _ = opt.unwrap_or_else(|| 2);
    |             ^^^^--------------------
@@ -9,7 +9,7 @@ LL |     let _ = opt.unwrap_or_else(|| 2);
    = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:36:13
+  --> $DIR/unnecessary_lazy_eval.rs:37:13
    |
 LL |     let _ = opt.unwrap_or_else(|| astronomers_pi);
    |             ^^^^---------------------------------
@@ -17,7 +17,7 @@ LL |     let _ = opt.unwrap_or_else(|| astronomers_pi);
    |                 help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:37:13
+  --> $DIR/unnecessary_lazy_eval.rs:38:13
    |
 LL |     let _ = opt.unwrap_or_else(|| ext_str.some_field);
    |             ^^^^-------------------------------------
@@ -25,7 +25,7 @@ LL |     let _ = opt.unwrap_or_else(|| ext_str.some_field);
    |                 help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:39:13
+  --> $DIR/unnecessary_lazy_eval.rs:40:13
    |
 LL |     let _ = opt.and_then(|_| ext_opt);
    |             ^^^^---------------------
@@ -33,7 +33,7 @@ LL |     let _ = opt.and_then(|_| ext_opt);
    |                 help: use `and(..)` instead: `and(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:40:13
+  --> $DIR/unnecessary_lazy_eval.rs:41:13
    |
 LL |     let _ = opt.or_else(|| ext_opt);
    |             ^^^^-------------------
@@ -41,7 +41,7 @@ LL |     let _ = opt.or_else(|| ext_opt);
    |                 help: use `or(..)` instead: `or(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:41:13
+  --> $DIR/unnecessary_lazy_eval.rs:42:13
    |
 LL |     let _ = opt.or_else(|| None);
    |             ^^^^----------------
@@ -49,7 +49,7 @@ LL |     let _ = opt.or_else(|| None);
    |                 help: use `or(..)` instead: `or(None)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:42:13
+  --> $DIR/unnecessary_lazy_eval.rs:43:13
    |
 LL |     let _ = opt.get_or_insert_with(|| 2);
    |             ^^^^------------------------
@@ -57,7 +57,7 @@ LL |     let _ = opt.get_or_insert_with(|| 2);
    |                 help: use `get_or_insert(..)` instead: `get_or_insert(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:43:13
+  --> $DIR/unnecessary_lazy_eval.rs:44:13
    |
 LL |     let _ = opt.ok_or_else(|| 2);
    |             ^^^^----------------
@@ -65,15 +65,23 @@ LL |     let _ = opt.ok_or_else(|| 2);
    |                 help: use `ok_or(..)` instead: `ok_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:44:13
+  --> $DIR/unnecessary_lazy_eval.rs:45:13
    |
 LL |     let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
    |             ^^^^^^^^^^^^^^^^^-------------------------------
    |                              |
    |                              help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))`
 
+error: unnecessary closure used with `bool::then`
+  --> $DIR/unnecessary_lazy_eval.rs:46:13
+   |
+LL |     let _ = cond.then(|| astronomers_pi);
+   |             ^^^^^-----------------------
+   |                  |
+   |                  help: use `then_some(..)` instead: `then_some(astronomers_pi)`
+
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:47:13
+  --> $DIR/unnecessary_lazy_eval.rs:49:13
    |
 LL |     let _ = Some(10).unwrap_or_else(|| 2);
    |             ^^^^^^^^^--------------------
@@ -81,7 +89,7 @@ LL |     let _ = Some(10).unwrap_or_else(|| 2);
    |                      help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:48:13
+  --> $DIR/unnecessary_lazy_eval.rs:50:13
    |
 LL |     let _ = Some(10).and_then(|_| ext_opt);
    |             ^^^^^^^^^---------------------
@@ -89,7 +97,7 @@ LL |     let _ = Some(10).and_then(|_| ext_opt);
    |                      help: use `and(..)` instead: `and(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:49:28
+  --> $DIR/unnecessary_lazy_eval.rs:51:28
    |
 LL |     let _: Option<usize> = None.or_else(|| ext_opt);
    |                            ^^^^^-------------------
@@ -97,7 +105,7 @@ LL |     let _: Option<usize> = None.or_else(|| ext_opt);
    |                                 help: use `or(..)` instead: `or(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:50:13
+  --> $DIR/unnecessary_lazy_eval.rs:52:13
    |
 LL |     let _ = None.get_or_insert_with(|| 2);
    |             ^^^^^------------------------
@@ -105,7 +113,7 @@ LL |     let _ = None.get_or_insert_with(|| 2);
    |                  help: use `get_or_insert(..)` instead: `get_or_insert(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:51:35
+  --> $DIR/unnecessary_lazy_eval.rs:53:35
    |
 LL |     let _: Result<usize, usize> = None.ok_or_else(|| 2);
    |                                   ^^^^^----------------
@@ -113,7 +121,7 @@ LL |     let _: Result<usize, usize> = None.ok_or_else(|| 2);
    |                                        help: use `ok_or(..)` instead: `ok_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:52:28
+  --> $DIR/unnecessary_lazy_eval.rs:54:28
    |
 LL |     let _: Option<usize> = None.or_else(|| None);
    |                            ^^^^^----------------
@@ -121,7 +129,7 @@ LL |     let _: Option<usize> = None.or_else(|| None);
    |                                 help: use `or(..)` instead: `or(None)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:55:13
+  --> $DIR/unnecessary_lazy_eval.rs:57:13
    |
 LL |     let _ = deep.0.unwrap_or_else(|| 2);
    |             ^^^^^^^--------------------
@@ -129,7 +137,7 @@ LL |     let _ = deep.0.unwrap_or_else(|| 2);
    |                    help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:56:13
+  --> $DIR/unnecessary_lazy_eval.rs:58:13
    |
 LL |     let _ = deep.0.and_then(|_| ext_opt);
    |             ^^^^^^^---------------------
@@ -137,7 +145,7 @@ LL |     let _ = deep.0.and_then(|_| ext_opt);
    |                    help: use `and(..)` instead: `and(ext_opt)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:57:13
+  --> $DIR/unnecessary_lazy_eval.rs:59:13
    |
 LL |     let _ = deep.0.or_else(|| None);
    |             ^^^^^^^----------------
@@ -145,7 +153,7 @@ LL |     let _ = deep.0.or_else(|| None);
    |                    help: use `or(..)` instead: `or(None)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:58:13
+  --> $DIR/unnecessary_lazy_eval.rs:60:13
    |
 LL |     let _ = deep.0.get_or_insert_with(|| 2);
    |             ^^^^^^^------------------------
@@ -153,7 +161,7 @@ LL |     let _ = deep.0.get_or_insert_with(|| 2);
    |                    help: use `get_or_insert(..)` instead: `get_or_insert(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:59:13
+  --> $DIR/unnecessary_lazy_eval.rs:61:13
    |
 LL |     let _ = deep.0.ok_or_else(|| 2);
    |             ^^^^^^^----------------
@@ -161,7 +169,7 @@ LL |     let _ = deep.0.ok_or_else(|| 2);
    |                    help: use `ok_or(..)` instead: `ok_or(2)`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:79:28
+  --> $DIR/unnecessary_lazy_eval.rs:81:28
    |
 LL |     let _: Option<usize> = None.or_else(|| Some(3));
    |                            ^^^^^-------------------
@@ -169,7 +177,7 @@ LL |     let _: Option<usize> = None.or_else(|| Some(3));
    |                                 help: use `or(..)` instead: `or(Some(3))`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:80:13
+  --> $DIR/unnecessary_lazy_eval.rs:82:13
    |
 LL |     let _ = deep.0.or_else(|| Some(3));
    |             ^^^^^^^-------------------
@@ -177,7 +185,7 @@ LL |     let _ = deep.0.or_else(|| Some(3));
    |                    help: use `or(..)` instead: `or(Some(3))`
 
 error: unnecessary closure used to substitute value for `Option::None`
-  --> $DIR/unnecessary_lazy_eval.rs:81:13
+  --> $DIR/unnecessary_lazy_eval.rs:83:13
    |
 LL |     let _ = opt.or_else(|| Some(3));
    |             ^^^^-------------------
@@ -185,7 +193,7 @@ LL |     let _ = opt.or_else(|| Some(3));
    |                 help: use `or(..)` instead: `or(Some(3))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:87:13
+  --> $DIR/unnecessary_lazy_eval.rs:89:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| 2);
    |             ^^^^^---------------------
@@ -193,7 +201,7 @@ LL |     let _ = res2.unwrap_or_else(|_| 2);
    |                  help: use `unwrap_or(..)` instead: `unwrap_or(2)`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:88:13
+  --> $DIR/unnecessary_lazy_eval.rs:90:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| astronomers_pi);
    |             ^^^^^----------------------------------
@@ -201,7 +209,7 @@ LL |     let _ = res2.unwrap_or_else(|_| astronomers_pi);
    |                  help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:89:13
+  --> $DIR/unnecessary_lazy_eval.rs:91:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| ext_str.some_field);
    |             ^^^^^--------------------------------------
@@ -209,7 +217,7 @@ LL |     let _ = res2.unwrap_or_else(|_| ext_str.some_field);
    |                  help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:111:35
+  --> $DIR/unnecessary_lazy_eval.rs:113:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(2));
    |                                   ^^^^--------------------
@@ -217,7 +225,7 @@ LL |     let _: Result<usize, usize> = res.and_then(|_| Err(2));
    |                                       help: use `and(..)` instead: `and(Err(2))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:112:35
+  --> $DIR/unnecessary_lazy_eval.rs:114:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
    |                                   ^^^^---------------------------------
@@ -225,7 +233,7 @@ LL |     let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
    |                                       help: use `and(..)` instead: `and(Err(astronomers_pi))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:113:35
+  --> $DIR/unnecessary_lazy_eval.rs:115:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
    |                                   ^^^^-------------------------------------
@@ -233,7 +241,7 @@ LL |     let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field))
    |                                       help: use `and(..)` instead: `and(Err(ext_str.some_field))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:115:35
+  --> $DIR/unnecessary_lazy_eval.rs:117:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(2));
    |                                   ^^^^------------------
@@ -241,7 +249,7 @@ LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(2));
    |                                       help: use `or(..)` instead: `or(Ok(2))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:116:35
+  --> $DIR/unnecessary_lazy_eval.rs:118:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
    |                                   ^^^^-------------------------------
@@ -249,7 +257,7 @@ LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
    |                                       help: use `or(..)` instead: `or(Ok(astronomers_pi))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:117:35
+  --> $DIR/unnecessary_lazy_eval.rs:119:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
    |                                   ^^^^-----------------------------------
@@ -257,7 +265,7 @@ LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
    |                                       help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
 
 error: unnecessary closure used to substitute value for `Result::Err`
-  --> $DIR/unnecessary_lazy_eval.rs:118:35
+  --> $DIR/unnecessary_lazy_eval.rs:120:35
    |
 LL |       let _: Result<usize, usize> = res.
    |  ___________________________________^
@@ -271,5 +279,5 @@ LL | |         or_else(|_| Ok(ext_str.some_field));
    |           |
    |           help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
 
-error: aborting due to 33 previous errors
+error: aborting due to 34 previous errors
 
index e9ff64431e19d154f614b9128a63028bb52c1ae3..c57c46736342b6814b1bb919126521ae516ca28f 100644 (file)
@@ -7,7 +7,8 @@
     unused_mut,
     dead_code,
     clippy::equatable_if_let,
-    clippy::manual_find
+    clippy::manual_find,
+    clippy::redundant_closure_call
 )]
 
 fn base() {
@@ -259,7 +260,7 @@ fn issue1924() {
         fn f(&mut self) -> Option<u32> {
             // Used as a field.
             for i in self.0.by_ref() {
-                if !(3..=7).contains(&i) {
+                if !(3..8).contains(&i) {
                     return Some(i);
                 }
             }
@@ -403,6 +404,47 @@ fn issue_8113() {
     }
 }
 
+fn fn_once_closure() {
+    let mut it = 0..10;
+    (|| {
+        for x in it {
+            if x % 2 == 0 {
+                break;
+            }
+        }
+    })();
+
+    fn f(_: impl FnOnce()) {}
+    let mut it = 0..10;
+    f(|| {
+        for x in it {
+            if x % 2 == 0 {
+                break;
+            }
+        }
+    });
+
+    fn f2(_: impl FnMut()) {}
+    let mut it = 0..10;
+    f2(|| {
+        for x in it.by_ref() {
+            if x % 2 == 0 {
+                break;
+            }
+        }
+    });
+
+    fn f3(_: fn()) {}
+    f3(|| {
+        let mut it = 0..10;
+        for x in it {
+            if x % 2 == 0 {
+                break;
+            }
+        }
+    })
+}
+
 fn main() {
     let mut it = 0..20;
     for _ in it {
index 03da39526b2fdd735098337004d9becf9c35b8bc..8b9a2dbcce3a7c62149ad36a72137281ce61ca53 100644 (file)
@@ -7,7 +7,8 @@
     unused_mut,
     dead_code,
     clippy::equatable_if_let,
-    clippy::manual_find
+    clippy::manual_find,
+    clippy::redundant_closure_call
 )]
 
 fn base() {
@@ -259,7 +260,7 @@ impl<T: Iterator<Item = u32>> S<T> {
         fn f(&mut self) -> Option<u32> {
             // Used as a field.
             while let Some(i) = self.0.next() {
-                if i < 3 || i > 7 {
+                if !(3..8).contains(&i) {
                     return Some(i);
                 }
             }
@@ -403,6 +404,47 @@ fn issue_8113() {
     }
 }
 
+fn fn_once_closure() {
+    let mut it = 0..10;
+    (|| {
+        while let Some(x) = it.next() {
+            if x % 2 == 0 {
+                break;
+            }
+        }
+    })();
+
+    fn f(_: impl FnOnce()) {}
+    let mut it = 0..10;
+    f(|| {
+        while let Some(x) = it.next() {
+            if x % 2 == 0 {
+                break;
+            }
+        }
+    });
+
+    fn f2(_: impl FnMut()) {}
+    let mut it = 0..10;
+    f2(|| {
+        while let Some(x) = it.next() {
+            if x % 2 == 0 {
+                break;
+            }
+        }
+    });
+
+    fn f3(_: fn()) {}
+    f3(|| {
+        let mut it = 0..10;
+        while let Some(x) = it.next() {
+            if x % 2 == 0 {
+                break;
+            }
+        }
+    })
+}
+
 fn main() {
     let mut it = 0..20;
     while let Some(..) = it.next() {
index 42859243855a6920d7ce5fdae686ae969dbbd0cf..3236765e1db0faa069607cd734d99a4c2db548d6 100644 (file)
@@ -1,5 +1,5 @@
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:15:5
+  --> $DIR/while_let_on_iterator.rs:16:5
    |
 LL |     while let Option::Some(x) = iter.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
@@ -7,138 +7,154 @@ LL |     while let Option::Some(x) = iter.next() {
    = note: `-D clippy::while-let-on-iterator` implied by `-D warnings`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:20:5
+  --> $DIR/while_let_on_iterator.rs:21:5
    |
 LL |     while let Some(x) = iter.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:25:5
+  --> $DIR/while_let_on_iterator.rs:26:5
    |
 LL |     while let Some(_) = iter.next() {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:101:9
+  --> $DIR/while_let_on_iterator.rs:102:9
    |
 LL |         while let Some([..]) = it.next() {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:108:9
+  --> $DIR/while_let_on_iterator.rs:109:9
    |
 LL |         while let Some([_x]) = it.next() {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:121:9
+  --> $DIR/while_let_on_iterator.rs:122:9
    |
 LL |         while let Some(x @ [_]) = it.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:141:9
+  --> $DIR/while_let_on_iterator.rs:142:9
    |
 LL |         while let Some(_) = y.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:198:9
+  --> $DIR/while_let_on_iterator.rs:199:9
    |
 LL |         while let Some(m) = it.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:209:5
+  --> $DIR/while_let_on_iterator.rs:210:5
    |
 LL |     while let Some(n) = it.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:211:9
+  --> $DIR/while_let_on_iterator.rs:212:9
    |
 LL |         while let Some(m) = it.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:220:9
+  --> $DIR/while_let_on_iterator.rs:221:9
    |
 LL |         while let Some(m) = it.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:229:9
+  --> $DIR/while_let_on_iterator.rs:230:9
    |
 LL |         while let Some(m) = it.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:246:9
+  --> $DIR/while_let_on_iterator.rs:247:9
    |
 LL |         while let Some(m) = it.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:261:13
+  --> $DIR/while_let_on_iterator.rs:262:13
    |
 LL |             while let Some(i) = self.0.next() {
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.by_ref()`
 
-error: manual `!RangeInclusive::contains` implementation
-  --> $DIR/while_let_on_iterator.rs:262:20
-   |
-LL |                 if i < 3 || i > 7 {
-   |                    ^^^^^^^^^^^^^^ help: use: `!(3..=7).contains(&i)`
-   |
-   = note: `-D clippy::manual-range-contains` implied by `-D warnings`
-
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:293:13
+  --> $DIR/while_let_on_iterator.rs:294:13
    |
 LL |             while let Some(i) = self.0.0.0.next() {
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.0.0.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:322:5
+  --> $DIR/while_let_on_iterator.rs:323:5
    |
 LL |     while let Some(n) = it.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:334:9
+  --> $DIR/while_let_on_iterator.rs:335:9
    |
 LL |         while let Some(x) = it.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:348:5
+  --> $DIR/while_let_on_iterator.rs:349:5
    |
 LL |     while let Some(x) = it.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:359:5
+  --> $DIR/while_let_on_iterator.rs:360:5
    |
 LL |     while let Some(x) = it.0.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:394:5
+  --> $DIR/while_let_on_iterator.rs:395:5
    |
 LL |     while let Some(x) = s.x.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in s.x.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:401:5
+  --> $DIR/while_let_on_iterator.rs:402:5
    |
 LL |     while let Some(x) = x[0].next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in x[0].by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:408:5
+  --> $DIR/while_let_on_iterator.rs:410:9
+   |
+LL |         while let Some(x) = it.next() {
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it`
+
+error: this loop could be written as a `for` loop
+  --> $DIR/while_let_on_iterator.rs:420:9
+   |
+LL |         while let Some(x) = it.next() {
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it`
+
+error: this loop could be written as a `for` loop
+  --> $DIR/while_let_on_iterator.rs:430:9
+   |
+LL |         while let Some(x) = it.next() {
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
+
+error: this loop could be written as a `for` loop
+  --> $DIR/while_let_on_iterator.rs:440:9
+   |
+LL |         while let Some(x) = it.next() {
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it`
+
+error: this loop could be written as a `for` loop
+  --> $DIR/while_let_on_iterator.rs:450:5
    |
 LL |     while let Some(..) = it.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
 
-error: aborting due to 23 previous errors
+error: aborting due to 26 previous errors
 
index b6f47ae906b73709b6096e5ffeeb4caa36dedab3..ef55f1c31a88bef188aba44728b4e9bdda0f890f 100644 (file)
@@ -199,7 +199,7 @@ mod super_imports {
         }
     }
 
-    mod should_be_replaced_futher_inside {
+    mod should_be_replaced_further_inside {
         fn insidefoo() {}
         mod inner {
             use super::insidefoo;
index eb404b7a3de5f661cbb2af6b428ca042830ac985..b81285142069b0e5d14557805ec8211ca5a4cff2 100644 (file)
@@ -200,7 +200,7 @@ fn with_super() {
         }
     }
 
-    mod should_be_replaced_futher_inside {
+    mod should_be_replaced_further_inside {
         fn insidefoo() {}
         mod inner {
             use super::*;
index a4bb26b9f900587151545521e862ec67db9701b2..ab98f9b4154ac0e78f3375090d0d08a3f77c5954 100644 (file)
@@ -8,15 +8,15 @@
             "problemMatcher": [],
             "group": {
                 "kind": "build",
-                "isDefault": true,
-            },
+                "isDefault": true
+            }
         },
         {
             "label": "cargo dev fmt",
             "type": "shell",
             "command": "cargo dev fmt",
             "problemMatcher": [],
-            "group": "none",
+            "group": "none"
         },
         {
             "label": "cargo uitest",
             "command": "cargo uitest",
             "options": {
                 "env": {
-                    "RUST_BACKTRACE": "1",
                     // This task will usually execute all UI tests inside `tests/ui` you can
                     // optionally uncomment the line below and only run a specific test.
                     //
-                    // See: https://github.com/rust-lang/rust-clippy/blob/master/doc/adding_lints.md#testing
+                    // See: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/adding_lints.md#testing
                     //
                     // "TESTNAME": "<TODO>",
-                },
+                    "RUST_BACKTRACE": "1"
+                }
             },
             "problemMatcher": [],
             "group": {
                 "kind": "test",
-                "isDefault": true,
+                "isDefault": true
             }
         },
         {
             "type": "shell",
             "command": "cargo test",
             "problemMatcher": [],
-            "group": "test",
+            "group": "test"
         },
         {
             "label": "cargo dev bless",
             "type": "shell",
             "command": "cargo dev bless",
             "problemMatcher": [],
-            "group": "none",
-        },
-    ],
+            "group": "none"
+        }
+    ]
 }
index 6183089911bdc0d35c0e5845b7a941da8129c185..c5d602ea3035f7e0a93a79df3bb715084681141c 100644 (file)
@@ -15,7 +15,8 @@ Otherwise, have a great day =^.^=
     <title>Clippy Lints</title>
 
     <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css"/>
-    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/styles/github.min.css"/>
+    <link id="githubLightHighlight" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github.min.css" disabled="true" />
+    <link id="githubDarkHighlight" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/styles/github-dark.min.css" disabled="true" />
 
     <!-- The files are not copied over into the Clippy project since they use the MPL-2.0 License -->
     <link rel="stylesheet" href="https://rust-lang.github.io/mdBook/css/variables.css"/>
@@ -561,8 +562,8 @@ Otherwise, have a great day =^.^=
     </a>
 
     <script src="https://cdnjs.cloudflare.com/ajax/libs/markdown-it/12.3.2/markdown-it.min.js"></script>
-    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/highlight.min.js"></script>
-    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.5.0/languages/rust.min.js"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/highlight.min.js"></script>
+    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.6.0/languages/rust.min.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.12/angular.min.js"></script>
     <script src="script.js"></script>
 </body>
index 6909fbcae09ff7c2d89fd748d140aee29ac8dbb9..366e7c8843f229507b1c3f3a4949d2939bd651a9 100644 (file)
@@ -7,7 +7,7 @@
             if (lang && hljs.getLanguage(lang)) {
                 try {
                     return '<pre class="hljs"><code>' +
-                        hljs.highlight(lang, str, true).value +
+                        hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
                         '</code></pre>';
                 } catch (__) {}
             }
@@ -365,6 +365,9 @@ function setTheme(theme, store) {
 
     document.getElementsByTagName("body")[0].className = theme;
 
+    document.getElementById("githubLightHighlight").disabled = enableNight || !enableHighlight;
+    document.getElementById("githubDarkHighlight").disabled = !enableNight && !enableAyu;
+
     document.getElementById("styleHighlight").disabled = !enableHighlight;
     document.getElementById("styleNight").disabled = !enableNight;
     document.getElementById("styleAyu").disabled = !enableAyu;