]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit '984330a6ee3c4d15626685d6dc8b7b759ff630bd' into clippyup
authorflip1995 <philipp.krones@embecosm.com>
Thu, 7 Apr 2022 17:39:59 +0000 (18:39 +0100)
committerflip1995 <philipp.krones@embecosm.com>
Fri, 8 Apr 2022 09:06:10 +0000 (10:06 +0100)
178 files changed:
CHANGELOG.md
Cargo.toml
clippy_dev/Cargo.toml
clippy_dev/src/bless.rs
clippy_dev/src/lib.rs
clippy_dev/src/lint.rs
clippy_dev/src/main.rs
clippy_dev/src/new_lint.rs
clippy_dev/src/update_lints.rs
clippy_lints/Cargo.toml
clippy_lints/src/casts/cast_abs_to_unsigned.rs [new file with mode: 0644]
clippy_lints/src/casts/cast_ptr_alignment.rs
clippy_lints/src/casts/mod.rs
clippy_lints/src/casts/unnecessary_cast.rs
clippy_lints/src/crate_in_macro_def.rs [new file with mode: 0644]
clippy_lints/src/doc.rs
clippy_lints/src/drop_forget_ref.rs
clippy_lints/src/empty_structs_with_brackets.rs [new file with mode: 0644]
clippy_lints/src/identity_op.rs
clippy_lints/src/indexing_slicing.rs
clippy_lints/src/lib.register_all.rs
clippy_lints/src/lib.register_complexity.rs
clippy_lints/src/lib.register_correctness.rs
clippy_lints/src/lib.register_lints.rs
clippy_lints/src/lib.register_restriction.rs
clippy_lints/src/lib.register_style.rs
clippy_lints/src/lib.register_suspicious.rs
clippy_lints/src/lib.rs
clippy_lints/src/loops/single_element_loop.rs
clippy_lints/src/map_unit_fn.rs
clippy_lints/src/matches/mod.rs
clippy_lints/src/matches/needless_match.rs
clippy_lints/src/methods/bytes_nth.rs
clippy_lints/src/methods/err_expect.rs [new file with mode: 0644]
clippy_lints/src/methods/implicit_clone.rs
clippy_lints/src/methods/iter_overeager_cloned.rs
clippy_lints/src/methods/map_identity.rs
clippy_lints/src/methods/mod.rs
clippy_lints/src/methods/needless_option_as_deref.rs [new file with mode: 0644]
clippy_lints/src/module_style.rs
clippy_lints/src/needless_option_as_deref.rs [deleted file]
clippy_lints/src/panic_unimplemented.rs
clippy_lints/src/ptr.rs
clippy_lints/src/transmute/mod.rs
clippy_lints/src/transmute/transmute_int_to_char.rs
clippy_lints/src/transmute/transmute_ref_to_ref.rs
clippy_lints/src/undocumented_unsafe_blocks.rs
clippy_lints/src/undropped_manually_drops.rs [deleted file]
clippy_lints/src/use_self.rs
clippy_lints/src/utils/conf.rs
clippy_lints/src/utils/internal_lints.rs
clippy_lints/src/utils/internal_lints/metadata_collector.rs
clippy_utils/Cargo.toml
clippy_utils/src/lib.rs
clippy_utils/src/msrvs.rs
clippy_utils/src/paths.rs
doc/release.md
rust-toolchain
src/driver.rs
tests/check-fmt.rs [new file with mode: 0644]
tests/fmt.rs [deleted file]
tests/ui-cargo/module_style/fail_mod/src/main.stderr
tests/ui-cargo/module_style/fail_no_mod/src/main.stderr
tests/ui-internal/check_clippy_version_attribute.stderr
tests/ui-toml/struct_excessive_bools/test.rs
tests/ui/auxiliary/proc_macro_unsafe.rs [new file with mode: 0644]
tests/ui/bytes_nth.stderr
tests/ui/case_sensitive_file_extension_comparisons.rs
tests/ui/cast.rs
tests/ui/cast_abs_to_unsigned.fixed [new file with mode: 0644]
tests/ui/cast_abs_to_unsigned.rs [new file with mode: 0644]
tests/ui/cast_abs_to_unsigned.stderr [new file with mode: 0644]
tests/ui/cast_alignment.rs
tests/ui/cast_alignment.stderr
tests/ui/crashes/ice-2774.rs
tests/ui/crashes/ice-6179.rs
tests/ui/crashes/ice-6792.rs
tests/ui/crashes/ice-7868.stderr
tests/ui/crashes/needless_lifetimes_impl_trait.rs
tests/ui/crashes/regressions.rs
tests/ui/crate_in_macro_def.fixed [new file with mode: 0644]
tests/ui/crate_in_macro_def.rs [new file with mode: 0644]
tests/ui/crate_in_macro_def.stderr [new file with mode: 0644]
tests/ui/default_numeric_fallback_f64.fixed
tests/ui/default_numeric_fallback_f64.rs
tests/ui/default_numeric_fallback_i32.fixed
tests/ui/default_numeric_fallback_i32.rs
tests/ui/drop_forget_copy.rs
tests/ui/drop_forget_copy.stderr
tests/ui/drop_non_drop.rs [new file with mode: 0644]
tests/ui/drop_non_drop.stderr [new file with mode: 0644]
tests/ui/drop_ref.rs
tests/ui/empty_structs_with_brackets.fixed [new file with mode: 0644]
tests/ui/empty_structs_with_brackets.rs [new file with mode: 0644]
tests/ui/empty_structs_with_brackets.stderr [new file with mode: 0644]
tests/ui/err_expect.fixed [new file with mode: 0644]
tests/ui/err_expect.rs [new file with mode: 0644]
tests/ui/err_expect.stderr [new file with mode: 0644]
tests/ui/fn_params_excessive_bools.rs
tests/ui/forget_non_drop.rs [new file with mode: 0644]
tests/ui/forget_non_drop.stderr [new file with mode: 0644]
tests/ui/forget_ref.rs
tests/ui/identity_op.rs
tests/ui/identity_op.stderr
tests/ui/implicit_clone.rs
tests/ui/indexing_slicing_index.rs
tests/ui/indexing_slicing_index.stderr
tests/ui/iter_nth_zero.fixed
tests/ui/iter_nth_zero.rs
tests/ui/iter_overeager_cloned.fixed
tests/ui/iter_overeager_cloned.rs
tests/ui/iter_overeager_cloned.stderr
tests/ui/large_types_passed_by_value.rs
tests/ui/let_and_return.rs
tests/ui/let_underscore_must_use.rs
tests/ui/manual_async_fn.fixed
tests/ui/manual_async_fn.rs
tests/ui/manual_unwrap_or.fixed
tests/ui/manual_unwrap_or.rs
tests/ui/map_identity.fixed
tests/ui/map_identity.rs
tests/ui/map_identity.stderr
tests/ui/map_unit_fn.rs
tests/ui/min_rust_version_attr.rs
tests/ui/min_rust_version_attr.stderr
tests/ui/missing_inline.rs
tests/ui/module_name_repetitions.rs
tests/ui/module_name_repetitions.stderr
tests/ui/modulo_arithmetic_integral_const.rs
tests/ui/modulo_arithmetic_integral_const.stderr
tests/ui/needless_arbitrary_self_type_unfixable.rs
tests/ui/needless_lifetimes.rs
tests/ui/needless_match.fixed
tests/ui/needless_match.rs
tests/ui/needless_match.stderr
tests/ui/needless_option_as_deref.fixed
tests/ui/needless_option_as_deref.rs
tests/ui/needless_option_as_deref.stderr
tests/ui/no_effect.rs
tests/ui/option_map_unit_fn_fixable.fixed
tests/ui/option_map_unit_fn_fixable.rs
tests/ui/option_map_unit_fn_fixable.stderr
tests/ui/or_then_unwrap.fixed
tests/ui/or_then_unwrap.rs
tests/ui/panicking_macros.rs
tests/ui/panicking_macros.stderr
tests/ui/ptr_arg.rs
tests/ui/recursive_format_impl.rs
tests/ui/redundant_allocation.rs
tests/ui/redundant_allocation_fixable.fixed
tests/ui/redundant_allocation_fixable.rs
tests/ui/redundant_clone.fixed
tests/ui/redundant_clone.rs
tests/ui/redundant_static_lifetimes.fixed
tests/ui/redundant_static_lifetimes.rs
tests/ui/result_map_unit_fn_fixable.fixed
tests/ui/result_map_unit_fn_fixable.rs
tests/ui/result_map_unit_fn_fixable.stderr
tests/ui/same_item_push.rs
tests/ui/single_element_loop.fixed
tests/ui/single_element_loop.rs
tests/ui/single_element_loop.stderr
tests/ui/trait_duplication_in_bounds.rs
tests/ui/transmute.rs
tests/ui/transmute.stderr
tests/ui/undocumented_unsafe_blocks.rs
tests/ui/undocumented_unsafe_blocks.stderr
tests/ui/unnecessary_cast.rs
tests/ui/unnecessary_cast_fixable.fixed
tests/ui/unnecessary_cast_fixable.rs
tests/ui/unsafe_derive_deserialize.rs
tests/ui/unsafe_removed_from_name.rs
tests/ui/unused_self.rs
tests/ui/use_self.fixed
tests/ui/use_self.rs
tests/ui/useless_attribute.fixed
tests/ui/useless_attribute.rs
tests/versioncheck.rs

index 88f71931d92b58432cfaa4e003422562bf9de0c0..b4097ea86a5185ce492067bad0ffb086733ad4c1 100644 (file)
@@ -6,11 +6,143 @@ document.
 
 ## Unreleased / In Rust Nightly
 
-[0eff589...master](https://github.com/rust-lang/rust-clippy/compare/0eff589...master)
+[57b3c4b...master](https://github.com/rust-lang/rust-clippy/compare/57b3c4b...master)
 
-## Rust 1.59 (beta)
+## Rust 1.60
 
-Current beta, release 2022-02-24
+Current stable, released 2022-04-07
+
+[0eff589...57b3c4b](https://github.com/rust-lang/rust-clippy/compare/0eff589...57b3c4b)
+
+### New Lints
+
+* [`single_char_lifetime_names`]
+  [#8236](https://github.com/rust-lang/rust-clippy/pull/8236)
+* [`iter_overeager_cloned`]
+  [#8203](https://github.com/rust-lang/rust-clippy/pull/8203)
+* [`transmute_undefined_repr`]
+  [#8398](https://github.com/rust-lang/rust-clippy/pull/8398)
+* [`default_union_representation`]
+  [#8289](https://github.com/rust-lang/rust-clippy/pull/8289)
+* [`manual_bits`]
+  [#8213](https://github.com/rust-lang/rust-clippy/pull/8213)
+* [`borrow_as_ptr`]
+  [#8210](https://github.com/rust-lang/rust-clippy/pull/8210)
+
+### Moves and Deprecations
+
+* Moved [`disallowed_methods`] and [`disallowed_types`] to `style` (now warn-by-default)
+  [#8261](https://github.com/rust-lang/rust-clippy/pull/8261)
+* Rename `ref_in_deref` to [`needless_borrow`]
+  [#8217](https://github.com/rust-lang/rust-clippy/pull/8217)
+* Moved [`mutex_atomic`] to `nursery` (now allow-by-default)
+  [#8260](https://github.com/rust-lang/rust-clippy/pull/8260)
+
+### Enhancements
+
+* [`ptr_arg`]: Now takes the argument usage into account and lints for mutable references
+  [#8271](https://github.com/rust-lang/rust-clippy/pull/8271)
+* [`unused_io_amount`]: Now supports async read and write traits
+  [#8179](https://github.com/rust-lang/rust-clippy/pull/8179)
+* [`while_let_on_iterator`]: Improved detection to catch more cases
+  [#8221](https://github.com/rust-lang/rust-clippy/pull/8221)
+* [`trait_duplication_in_bounds`]: Now covers trait functions with `Self` bounds
+  [#8252](https://github.com/rust-lang/rust-clippy/pull/8252)
+* [`unwrap_used`]: Now works for `.get(i).unwrap()` and `.get_mut(i).unwrap()`
+  [#8372](https://github.com/rust-lang/rust-clippy/pull/8372)
+* [`map_clone`]: The suggestion takes `msrv` into account
+  [#8280](https://github.com/rust-lang/rust-clippy/pull/8280)
+* [`manual_bits`] and [`borrow_as_ptr`]: Now track the `clippy::msrv` attribute
+  [#8280](https://github.com/rust-lang/rust-clippy/pull/8280)
+* [`disallowed_methods`]: Now works for methods on primitive types
+  [#8112](https://github.com/rust-lang/rust-clippy/pull/8112)
+* [`not_unsafe_ptr_arg_deref`]: Now works for type aliases
+  [#8273](https://github.com/rust-lang/rust-clippy/pull/8273)
+* [`needless_question_mark`]: Now works for async functions
+  [#8311](https://github.com/rust-lang/rust-clippy/pull/8311)
+* [`iter_not_returning_iterator`]: Now handles type projections
+  [#8228](https://github.com/rust-lang/rust-clippy/pull/8228)
+* [`wrong_self_convention`]: Now detects wrong `self` references in more cases
+  [#8208](https://github.com/rust-lang/rust-clippy/pull/8208)
+* [`single_match`]: Now works for `match` statements with tuples
+  [#8322](https://github.com/rust-lang/rust-clippy/pull/8322)
+
+### False Positive Fixes
+
+* [`erasing_op`]: No longer triggers if the output type changes
+  [#8204](https://github.com/rust-lang/rust-clippy/pull/8204)
+* [`if_same_then_else`]: No longer triggers for `if let` statements
+  [#8297](https://github.com/rust-lang/rust-clippy/pull/8297)
+* [`manual_memcpy`]: No longer lints on `VecDeque`
+  [#8226](https://github.com/rust-lang/rust-clippy/pull/8226)
+* [`trait_duplication_in_bounds`]: Now takes path segments into account
+  [#8315](https://github.com/rust-lang/rust-clippy/pull/8315)
+* [`deref_addrof`]: No longer lints when the dereference or borrow occurs in different a context
+  [#8268](https://github.com/rust-lang/rust-clippy/pull/8268)
+* [`type_repetition_in_bounds`]: Now checks for full equality to prevent false positives
+  [#8224](https://github.com/rust-lang/rust-clippy/pull/8224)
+* [`ptr_arg`]: No longer lint for mutable references in traits
+  [#8369](https://github.com/rust-lang/rust-clippy/pull/8369)
+* [`implicit_clone`]: No longer lints for double references
+  [#8231](https://github.com/rust-lang/rust-clippy/pull/8231)
+* [`needless_lifetimes`]: No longer lints lifetimes for explicit `self` types
+  [#8278](https://github.com/rust-lang/rust-clippy/pull/8278)
+* [`op_ref`]: No longer lints in `BinOp` impl if that can cause recursion
+  [#8298](https://github.com/rust-lang/rust-clippy/pull/8298)
+* [`enum_variant_names`]: No longer triggers for empty variant names
+  [#8329](https://github.com/rust-lang/rust-clippy/pull/8329)
+* [`redundant_closure`]: No longer lints for `Arc<T>` or `Rc<T>`
+  [#8193](https://github.com/rust-lang/rust-clippy/pull/8193)
+* [`iter_not_returning_iterator`]: No longer lints on trait implementations but therefore on trait definitions
+  [#8228](https://github.com/rust-lang/rust-clippy/pull/8228)
+* [`single_match`]: No longer lints on exhaustive enum patterns without a wildcard
+  [#8322](https://github.com/rust-lang/rust-clippy/pull/8322)
+* [`manual_swap`]: No longer lints on cases that involve automatic dereferences
+  [#8220](https://github.com/rust-lang/rust-clippy/pull/8220)
+* [`useless_format`]: Now works for implicit named arguments
+  [#8295](https://github.com/rust-lang/rust-clippy/pull/8295)
+
+### Suggestion Fixes/Improvements
+
+* [`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
+  [#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)
+* [`manual_memcpy`]: Suggests `copy_from_slice` when applicable
+  [#8226](https://github.com/rust-lang/rust-clippy/pull/8226)
+* [`or_fun_call`]: Improved suggestion display for long arguments
+  [#8292](https://github.com/rust-lang/rust-clippy/pull/8292)
+* [`unnecessary_cast`]: Now correctly includes the sign
+  [#8350](https://github.com/rust-lang/rust-clippy/pull/8350)
+* [`cmp_owned`]: No longer flips the comparison order
+  [#8299](https://github.com/rust-lang/rust-clippy/pull/8299)
+* [`explicit_counter_loop`]: Now correctly suggests `iter()` on references
+  [#8382](https://github.com/rust-lang/rust-clippy/pull/8382)
+
+### ICE Fixes
+
+* [`manual_split_once`]
+  [#8250](https://github.com/rust-lang/rust-clippy/pull/8250)
+
+### Documentation Improvements
+
+* [`map_flatten`]: Add documentation for the `Option` type
+  [#8354](https://github.com/rust-lang/rust-clippy/pull/8354)
+* Document that Clippy's driver might use a different code generation than rustc
+  [#8037](https://github.com/rust-lang/rust-clippy/pull/8037)
+* Clippy's lint list will now automatically focus the search box
+  [#8343](https://github.com/rust-lang/rust-clippy/pull/8343)
+
+### Others
+
+* Clippy now warns if we find multiple Clippy config files exist
+  [#8326](https://github.com/rust-lang/rust-clippy/pull/8326)
+
+## Rust 1.59
+
+Released 2022-02-24
 
 [e181011...0eff589](https://github.com/rust-lang/rust-clippy/compare/e181011...0eff589)
 
@@ -174,7 +306,7 @@ Current beta, release 2022-02-24
 
 ## Rust 1.58
 
-Current stable, released 2022-01-13
+Released 2022-01-13
 
 [00e31fa...e181011](https://github.com/rust-lang/rust-clippy/compare/00e31fa...e181011)
 
@@ -3069,6 +3201,7 @@ Released 2018-09-13
 [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
 [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
 [`case_sensitive_file_extension_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#case_sensitive_file_extension_comparisons
+[`cast_abs_to_unsigned`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_abs_to_unsigned
 [`cast_enum_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_constructor
 [`cast_enum_truncation`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_enum_truncation
 [`cast_lossless`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_lossless
@@ -3097,6 +3230,7 @@ Released 2018-09-13
 [`comparison_chain`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_chain
 [`comparison_to_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#comparison_to_empty
 [`copy_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#copy_iterator
+[`crate_in_macro_def`]: https://rust-lang.github.io/rust-clippy/master/index.html#crate_in_macro_def
 [`create_dir`]: https://rust-lang.github.io/rust-clippy/master/index.html#create_dir
 [`crosspointer_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#crosspointer_transmute
 [`dbg_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#dbg_macro
@@ -3123,6 +3257,7 @@ Released 2018-09-13
 [`double_neg`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_neg
 [`double_parens`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_parens
 [`drop_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_copy
+[`drop_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_non_drop
 [`drop_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#drop_ref
 [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
 [`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
@@ -3130,12 +3265,14 @@ Released 2018-09-13
 [`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
 [`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
 [`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop
+[`empty_structs_with_brackets`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_structs_with_brackets
 [`enum_clike_unportable_variant`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_clike_unportable_variant
 [`enum_glob_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_glob_use
 [`enum_variant_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
 [`eq_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#eq_op
 [`equatable_if_let`]: https://rust-lang.github.io/rust-clippy/master/index.html#equatable_if_let
 [`erasing_op`]: https://rust-lang.github.io/rust-clippy/master/index.html#erasing_op
+[`err_expect`]: https://rust-lang.github.io/rust-clippy/master/index.html#err_expect
 [`eval_order_dependence`]: https://rust-lang.github.io/rust-clippy/master/index.html#eval_order_dependence
 [`excessive_precision`]: https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision
 [`exhaustive_enums`]: https://rust-lang.github.io/rust-clippy/master/index.html#exhaustive_enums
@@ -3174,6 +3311,7 @@ Released 2018-09-13
 [`for_kv_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_kv_map
 [`for_loops_over_fallibles`]: https://rust-lang.github.io/rust-clippy/master/index.html#for_loops_over_fallibles
 [`forget_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_copy
+[`forget_non_drop`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_non_drop
 [`forget_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#forget_ref
 [`format_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#format_in_format_args
 [`from_iter_instead_of_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#from_iter_instead_of_collect
index 123af23881b623ffd7ba83b8f1a259db3fa49b40..dd6518d5241b5fad11f356ae2fa089f3218a109e 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.61"
+version = "0.1.62"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
@@ -21,13 +21,12 @@ name = "clippy-driver"
 path = "src/driver.rs"
 
 [dependencies]
-clippy_lints = { version = "0.1", path = "clippy_lints" }
+clippy_lints = { path = "clippy_lints" }
 semver = "1.0"
-rustc_tools_util = { version = "0.2", path = "rustc_tools_util" }
+rustc_tools_util = { path = "rustc_tools_util" }
 tempfile = { version = "3.2", optional = true }
 
 [dev-dependencies]
-cargo_metadata = "0.14"
 compiletest_rs = { version = "0.7.1", features = ["tmp"] }
 tester = "0.9"
 regex = "1.5"
@@ -45,7 +44,7 @@ derive-new = "0.5"
 if_chain = "1.0"
 itertools = "0.10.1"
 quote = "1.0"
-serde = { version = "1.0", features = ["derive"] }
+serde = { version = "1.0.125", features = ["derive"] }
 syn = { version = "1.0", features = ["full"] }
 futures = "0.3"
 parking_lot = "0.11.2"
index d133e8cddabc729b96ec2d2617401a771883660a..c2ebba0683ca9b85ef0dd6bcdb11f33a768f2e80 100644 (file)
@@ -4,15 +4,17 @@ version = "0.0.1"
 edition = "2021"
 
 [dependencies]
-bytecount = "0.6"
 clap = "2.33"
 indoc = "1.0"
 itertools = "0.10.1"
 opener = "0.5"
-regex = "1.5"
 shell-escape = "0.1"
+tempfile = "3.3"
 walkdir = "2.3"
-cargo_metadata = "0.14"
 
 [features]
 deny-warnings = []
+
+[package.metadata.rust-analyzer]
+# This package uses #[feature(rustc_private)]
+rustc_private = true
index b0fb39e8169968aa57624a6d988879a11021b02c..8e5c739afe05ae85e3a00cee769678233806b5ba 100644 (file)
@@ -1,22 +1,15 @@
 //! `bless` updates the reference files in the repo with changed output files
 //! from the last test run.
 
+use crate::cargo_clippy_path;
 use std::ffi::OsStr;
 use std::fs;
 use std::lazy::SyncLazy;
 use std::path::{Path, PathBuf};
 use walkdir::{DirEntry, WalkDir};
 
-#[cfg(not(windows))]
-static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
-#[cfg(windows)]
-static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe";
-
-static CLIPPY_BUILD_TIME: SyncLazy<Option<std::time::SystemTime>> = SyncLazy::new(|| {
-    let mut path = std::env::current_exe().unwrap();
-    path.set_file_name(CARGO_CLIPPY_EXE);
-    fs::metadata(path).ok()?.modified().ok()
-});
+static CLIPPY_BUILD_TIME: SyncLazy<Option<std::time::SystemTime>> =
+    SyncLazy::new(|| cargo_clippy_path().metadata().ok()?.modified().ok());
 
 /// # Panics
 ///
index 59fde447547145f3093b5054112bd58dd6ec8b7e..9c6d754b400fc0f41595cfd8a4a745a1de926dd9 100644 (file)
@@ -1,8 +1,12 @@
+#![feature(let_else)]
 #![feature(once_cell)]
+#![feature(rustc_private)]
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 // warn on lints, that are included in `rust-lang/rust`s bootstrap
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
+extern crate rustc_lexer;
+
 use std::path::PathBuf;
 
 pub mod bless;
 pub mod setup;
 pub mod update_lints;
 
+#[cfg(not(windows))]
+static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
+#[cfg(windows)]
+static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe";
+
+/// Returns the path to the `cargo-clippy` binary
+#[must_use]
+pub fn cargo_clippy_path() -> PathBuf {
+    let mut path = std::env::current_exe().expect("failed to get current executable name");
+    path.set_file_name(CARGO_CLIPPY_EXE);
+    path
+}
+
 /// Returns the path to the Clippy project directory
 ///
 /// # Panics
index b8287980a4bacea5df7cadbe2a50621eac7e6131..1bc1a39542db5c00bb0ebc1dd854d4b90b5aa859 100644 (file)
@@ -1,19 +1,52 @@
-use std::process::{self, Command};
+use crate::cargo_clippy_path;
+use std::process::{self, Command, ExitStatus};
+use std::{fs, io};
 
-pub fn run(filename: &str) {
-    let code = Command::new("cargo")
-        .args(["run", "--bin", "clippy-driver", "--"])
-        .args(["-L", "./target/debug"])
-        .args(["-Z", "no-codegen"])
-        .args(["--edition", "2021"])
-        .arg(filename)
-        .status()
-        .expect("failed to run cargo")
-        .code();
-
-    if code.is_none() {
-        eprintln!("Killed by signal");
+fn exit_if_err(status: io::Result<ExitStatus>) {
+    match status.expect("failed to run command").code() {
+        Some(0) => {},
+        Some(n) => process::exit(n),
+        None => {
+            eprintln!("Killed by signal");
+            process::exit(1);
+        },
     }
+}
+
+pub fn run(path: &str) {
+    let is_file = match fs::metadata(path) {
+        Ok(metadata) => metadata.is_file(),
+        Err(e) => {
+            eprintln!("Failed to read {path}: {e:?}");
+            process::exit(1);
+        },
+    };
+
+    if is_file {
+        exit_if_err(
+            Command::new("cargo")
+                .args(["run", "--bin", "clippy-driver", "--"])
+                .args(["-L", "./target/debug"])
+                .args(["-Z", "no-codegen"])
+                .args(["--edition", "2021"])
+                .arg(path)
+                .status(),
+        );
+    } else {
+        exit_if_err(Command::new("cargo").arg("build").status());
 
-    process::exit(code.unwrap_or(1));
+        // Run in a tempdir as changes to clippy do not retrigger linting
+        let target = tempfile::Builder::new()
+            .prefix("clippy")
+            .tempdir()
+            .expect("failed to create tempdir");
+
+        let status = Command::new(cargo_clippy_path())
+            .current_dir(path)
+            .env("CARGO_TARGET_DIR", target.as_ref())
+            .status();
+
+        target.close().expect("failed to remove tempdir");
+        exit_if_err(status);
+    }
 }
index 30a241c8ba151e1cfa3b1385156f13ddd892eb06..b1fe35a0243f04e4887cdb020edd0de32040c7eb 100644 (file)
@@ -4,6 +4,7 @@
 
 use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
 use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints};
+use indoc::indoc;
 fn main() {
     let matches = get_clap_config();
 
@@ -56,8 +57,8 @@ fn main() {
             serve::run(port, lint);
         },
         ("lint", Some(matches)) => {
-            let filename = matches.value_of("filename").unwrap();
-            lint::run(filename);
+            let path = matches.value_of("path").unwrap();
+            lint::run(path);
         },
         _ => {},
     }
@@ -225,11 +226,20 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
         )
         .subcommand(
             SubCommand::with_name("lint")
-                .about("Manually run clippy on a file")
+                .about("Manually run clippy on a file or package")
+                .after_help(indoc! {"
+                    EXAMPLES
+                        Lint a single file:
+                            cargo dev lint tests/ui/attrs.rs
+
+                        Lint a package directory:
+                            cargo dev lint tests/ui-cargo/wildcard_dependencies/fail
+                            cargo dev lint ~/my-project
+                "})
                 .arg(
-                    Arg::with_name("filename")
+                    Arg::with_name("path")
                         .required(true)
-                        .help("The path to a file to lint"),
+                        .help("The path to a file or package directory to lint"),
                 ),
         )
         .get_matches()
index 59658b42c79b14405773817052214034e13cedbc..7a3fd1317619e39d3021f8dafe8e0281dc4a99a1 100644 (file)
@@ -133,15 +133,23 @@ fn to_camel_case(name: &str) -> String {
 }
 
 fn get_stabilisation_version() -> String {
-    let mut command = cargo_metadata::MetadataCommand::new();
-    command.no_deps();
-    if let Ok(metadata) = command.exec() {
-        if let Some(pkg) = metadata.packages.iter().find(|pkg| pkg.name == "clippy") {
-            return format!("{}.{}.0", pkg.version.minor, pkg.version.patch);
-        }
+    fn parse_manifest(contents: &str) -> Option<String> {
+        let version = contents
+            .lines()
+            .filter_map(|l| l.split_once('='))
+            .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))?;
+        let Some(("0", version)) = version.get(1..version.len() - 1)?.split_once('.') else {
+            return None;
+        };
+        let (minor, patch) = version.split_once('.')?;
+        Some(format!(
+            "{}.{}.0",
+            minor.parse::<u32>().ok()?,
+            patch.parse::<u32>().ok()?
+        ))
     }
-
-    String::from("<TODO set version(see doc/adding_lints.md)>")
+    let contents = fs::read_to_string("Cargo.toml").expect("Unable to read `Cargo.toml`");
+    parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`")
 }
 
 fn get_test_file_contents(lint_name: &str, header_commands: Option<&str>) -> String {
index d368ef1f46a2a98866e6aedcf32581c9ce17c047..59db51fbfac5149fec5158a137c91e94fa86bf4b 100644 (file)
@@ -1,9 +1,9 @@
+use core::fmt::Write;
 use itertools::Itertools;
-use regex::Regex;
+use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
 use std::collections::HashMap;
 use std::ffi::OsStr;
 use std::fs;
-use std::lazy::SyncLazy;
 use std::path::Path;
 use walkdir::WalkDir;
 
      // Use that command to update this file and do not edit by hand.\n\
      // Manual edits will be overwritten.\n\n";
 
-static DEC_CLIPPY_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
-    Regex::new(
-        r#"(?x)
-    declare_clippy_lint!\s*[\{(]
-    (?:\s+///.*)*
-    (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])?
-    \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
-    (?P<cat>[a-z_]+)\s*,\s*
-    "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
-"#,
-    )
-    .unwrap()
-});
-
-static DEC_DEPRECATED_LINT_RE: SyncLazy<Regex> = SyncLazy::new(|| {
-    Regex::new(
-        r#"(?x)
-    declare_deprecated_lint!\s*[{(]\s*
-    (?:\s+///.*)*
-    (?:\s*\#\[clippy::version\s*=\s*"[^"]*"\])?
-    \s+pub\s+(?P<name>[A-Z_][A-Z_0-9]*)\s*,\s*
-    "(?P<desc>(?:[^"\\]+|\\(?s).(?-s))*)"\s*[})]
-"#,
-    )
-    .unwrap()
-});
-static NL_ESCAPE_RE: SyncLazy<Regex> = SyncLazy::new(|| Regex::new(r#"\\\n\s*"#).unwrap());
-
-static DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
+const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
 
 #[derive(Clone, Copy, PartialEq)]
 pub enum UpdateMode {
@@ -60,60 +32,52 @@ pub enum UpdateMode {
 /// Panics if a file path could not read from or then written to
 #[allow(clippy::too_many_lines)]
 pub fn run(update_mode: UpdateMode) {
-    let lint_list: Vec<Lint> = gather_all().collect();
+    let (lints, deprecated_lints) = gather_all();
 
-    let internal_lints = Lint::internal_lints(&lint_list);
-    let deprecated_lints = Lint::deprecated_lints(&lint_list);
-    let usable_lints = Lint::usable_lints(&lint_list);
+    let internal_lints = Lint::internal_lints(&lints);
+    let usable_lints = Lint::usable_lints(&lints);
     let mut sorted_usable_lints = usable_lints.clone();
     sorted_usable_lints.sort_by_key(|lint| lint.name.clone());
 
-    let usable_lint_count = round_to_fifty(usable_lints.len());
-
-    let mut file_change = false;
-
-    file_change |= replace_region_in_file(
+    replace_region_in_file(
+        update_mode,
         Path::new("README.md"),
-        &format!(
-            r#"\[There are over \d+ lints included in this crate!\]\({}\)"#,
-            DOCS_LINK
-        ),
-        "",
-        true,
-        update_mode == UpdateMode::Change,
-        || {
-            vec![format!(
-                "[There are over {} lints included in this crate!]({})",
-                usable_lint_count, DOCS_LINK
-            )]
+        "[There are over ",
+        " lints included in this crate!]",
+        |res| {
+            write!(res, "{}", round_to_fifty(usable_lints.len())).unwrap();
         },
-    )
-    .changed;
+    );
 
-    file_change |= replace_region_in_file(
+    replace_region_in_file(
+        update_mode,
         Path::new("CHANGELOG.md"),
-        "<!-- begin autogenerated links to lint list -->",
+        "<!-- begin autogenerated links to lint list -->\n",
         "<!-- end autogenerated links to lint list -->",
-        false,
-        update_mode == UpdateMode::Change,
-        || gen_changelog_lint_list(usable_lints.iter().chain(deprecated_lints.iter())),
-    )
-    .changed;
+        |res| {
+            for lint in usable_lints
+                .iter()
+                .map(|l| &l.name)
+                .chain(deprecated_lints.iter().map(|l| &l.name))
+                .sorted()
+            {
+                writeln!(res, "[`{}`]: {}#{}", lint, DOCS_LINK, lint).unwrap();
+            }
+        },
+    );
 
     // This has to be in lib.rs, otherwise rustfmt doesn't work
-    file_change |= replace_region_in_file(
+    replace_region_in_file(
+        update_mode,
         Path::new("clippy_lints/src/lib.rs"),
-        "begin lints modules",
-        "end lints modules",
-        false,
-        update_mode == UpdateMode::Change,
-        || gen_modules_list(usable_lints.iter()),
-    )
-    .changed;
-
-    if file_change && update_mode == UpdateMode::Check {
-        exit_with_failure();
-    }
+        "// begin lints modules, do not remove this comment, it’s used in `update_lints`\n",
+        "// end lints modules, do not remove this comment, it’s used in `update_lints`",
+        |res| {
+            for lint_mod in usable_lints.iter().map(|l| &l.module).unique().sorted() {
+                writeln!(res, "mod {};", lint_mod).unwrap();
+            }
+        },
+    );
 
     process_file(
         "clippy_lints/src/lib.register_lints.rs",
@@ -123,7 +87,7 @@ pub fn run(update_mode: UpdateMode) {
     process_file(
         "clippy_lints/src/lib.deprecated.rs",
         update_mode,
-        &gen_deprecated(deprecated_lints.iter()),
+        &gen_deprecated(&deprecated_lints),
     );
 
     let all_group_lints = usable_lints.iter().filter(|l| {
@@ -146,15 +110,12 @@ pub fn run(update_mode: UpdateMode) {
 }
 
 pub fn print_lints() {
-    let lint_list: Vec<Lint> = gather_all().collect();
+    let (lint_list, _) = gather_all();
     let usable_lints = Lint::usable_lints(&lint_list);
     let usable_lint_count = usable_lints.len();
     let grouped_by_lint_group = Lint::by_lint_group(usable_lints.into_iter());
 
     for (lint_group, mut lints) in grouped_by_lint_group {
-        if lint_group == "Deprecated" {
-            continue;
-        }
         println!("\n## {}", lint_group);
 
         lints.sort_by_key(|l| l.name.clone());
@@ -198,19 +159,17 @@ struct Lint {
     name: String,
     group: String,
     desc: String,
-    deprecation: Option<String>,
     module: String,
 }
 
 impl Lint {
     #[must_use]
-    fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &str) -> Self {
+    fn new(name: &str, group: &str, desc: &str, module: &str) -> Self {
         Self {
             name: name.to_lowercase(),
-            group: group.to_string(),
-            desc: NL_ESCAPE_RE.replace(&desc.replace("\\\"", "\""), "").to_string(),
-            deprecation: deprecation.map(ToString::to_string),
-            module: module.to_string(),
+            group: group.into(),
+            desc: remove_line_splices(desc),
+            module: module.into(),
         }
     }
 
@@ -219,7 +178,7 @@ fn new(name: &str, group: &str, desc: &str, deprecation: Option<&str>, module: &
     fn usable_lints(lints: &[Self]) -> Vec<Self> {
         lints
             .iter()
-            .filter(|l| l.deprecation.is_none() && !l.group.starts_with("internal"))
+            .filter(|l| !l.group.starts_with("internal"))
             .cloned()
             .collect()
     }
@@ -230,12 +189,6 @@ fn internal_lints(lints: &[Self]) -> Vec<Self> {
         lints.iter().filter(|l| l.group == "internal").cloned().collect()
     }
 
-    /// Returns all deprecated lints
-    #[must_use]
-    fn deprecated_lints(lints: &[Self]) -> Vec<Self> {
-        lints.iter().filter(|l| l.deprecation.is_some()).cloned().collect()
-    }
-
     /// Returns the lints in a `HashMap`, grouped by the different lint groups
     #[must_use]
     fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>> {
@@ -243,6 +196,20 @@ fn by_lint_group(lints: impl Iterator<Item = Self>) -> HashMap<String, Vec<Self>
     }
 }
 
+#[derive(Clone, PartialEq, Debug)]
+struct DeprecatedLint {
+    name: String,
+    reason: String,
+}
+impl DeprecatedLint {
+    fn new(name: &str, reason: &str) -> Self {
+        Self {
+            name: name.to_lowercase(),
+            reason: remove_line_splices(reason),
+        }
+    }
+}
+
 /// Generates the code for registering a group
 fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lint>) -> String {
     let mut details: Vec<_> = lints.map(|l| (&l.module, l.name.to_uppercase())).collect();
@@ -262,32 +229,12 @@ fn gen_lint_group_list<'a>(group_name: &str, lints: impl Iterator<Item = &'a Lin
     output
 }
 
-/// Generates the module declarations for `lints`
-#[must_use]
-fn gen_modules_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
-    lints
-        .map(|l| &l.module)
-        .unique()
-        .map(|module| format!("mod {};", module))
-        .sorted()
-        .collect::<Vec<String>>()
-}
-
-/// Generates the list of lint links at the bottom of the CHANGELOG
-#[must_use]
-fn gen_changelog_lint_list<'a>(lints: impl Iterator<Item = &'a Lint>) -> Vec<String> {
-    lints
-        .sorted_by_key(|l| &l.name)
-        .map(|l| format!("[`{}`]: {}#{}", l.name, DOCS_LINK, l.name))
-        .collect()
-}
-
 /// Generates the `register_removed` code
 #[must_use]
-fn gen_deprecated<'a>(lints: impl Iterator<Item = &'a Lint>) -> String {
+fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
     let mut output = GENERATED_FILE_COMMENT.to_string();
     output.push_str("{\n");
-    for Lint { name, deprecation, .. } in lints {
+    for lint in lints {
         output.push_str(&format!(
             concat!(
                 "    store.register_removed(\n",
@@ -295,8 +242,7 @@ fn gen_deprecated<'a>(lints: impl Iterator<Item = &'a Lint>) -> String {
                 "        \"{}\",\n",
                 "    );\n"
             ),
-            name,
-            deprecation.as_ref().expect("`lints` are deprecated")
+            lint.name, lint.reason,
         ));
     }
     output.push_str("}\n");
@@ -330,61 +276,136 @@ fn gen_register_lint_list<'a>(
     output
 }
 
-/// Gathers all files in `src/clippy_lints` and gathers all lints inside
-fn gather_all() -> impl Iterator<Item = Lint> {
-    lint_files().flat_map(|f| gather_from_file(&f))
-}
+/// Gathers all lints defined in `clippy_lints/src`
+fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>) {
+    let mut lints = Vec::with_capacity(1000);
+    let mut deprecated_lints = Vec::with_capacity(50);
+    let root_path = clippy_project_root().join("clippy_lints/src");
 
-fn gather_from_file(dir_entry: &walkdir::DirEntry) -> impl Iterator<Item = Lint> {
-    let content = fs::read_to_string(dir_entry.path()).unwrap();
-    let path = dir_entry.path();
-    let filename = path.file_stem().unwrap();
-    let path_buf = path.with_file_name(filename);
-    let mut rel_path = path_buf
-        .strip_prefix(clippy_project_root().join("clippy_lints/src"))
-        .expect("only files in `clippy_lints/src` should be looked at");
-    // If the lints are stored in mod.rs, we get the module name from
-    // the containing directory:
-    if filename == "mod" {
-        rel_path = rel_path.parent().unwrap();
-    }
+    for (rel_path, file) in WalkDir::new(&root_path)
+        .into_iter()
+        .map(Result::unwrap)
+        .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
+        .map(|f| (f.path().strip_prefix(&root_path).unwrap().to_path_buf(), f))
+    {
+        let path = file.path();
+        let contents =
+            fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e));
+        let module = rel_path
+            .components()
+            .map(|c| c.as_os_str().to_str().unwrap())
+            .collect::<Vec<_>>()
+            .join("::");
+
+        // If the lints are stored in mod.rs, we get the module name from
+        // the containing directory:
+        let module = if let Some(module) = module.strip_suffix("::mod.rs") {
+            module
+        } else {
+            module.strip_suffix(".rs").unwrap_or(&module)
+        };
 
-    let module = rel_path
-        .components()
-        .map(|c| c.as_os_str().to_str().unwrap())
-        .collect::<Vec<_>>()
-        .join("::");
+        if module == "deprecated_lints" {
+            parse_deprecated_contents(&contents, &mut deprecated_lints);
+        } else {
+            parse_contents(&contents, module, &mut lints);
+        }
+    }
+    (lints, deprecated_lints)
+}
 
-    parse_contents(&content, &module)
+macro_rules! match_tokens {
+    ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => {
+         {
+            $($(let $capture =)? if let Some((TokenKind::$token $({$($fields)*})?, _x)) = $iter.next() {
+                _x
+            } else {
+                continue;
+            };)*
+            #[allow(clippy::unused_unit)]
+            { ($($($capture,)?)*) }
+        }
+    }
 }
 
-fn parse_contents(content: &str, module: &str) -> impl Iterator<Item = Lint> {
-    let lints = DEC_CLIPPY_LINT_RE
-        .captures_iter(content)
-        .map(|m| Lint::new(&m["name"], &m["cat"], &m["desc"], None, module));
-    let deprecated = DEC_DEPRECATED_LINT_RE
-        .captures_iter(content)
-        .map(|m| Lint::new(&m["name"], "Deprecated", &m["desc"], Some(&m["desc"]), module));
-    // Removing the `.collect::<Vec<Lint>>().into_iter()` causes some lifetime issues due to the map
-    lints.chain(deprecated).collect::<Vec<Lint>>().into_iter()
+/// Parse a source file looking for `declare_clippy_lint` macro invocations.
+fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
+    let mut offset = 0usize;
+    let mut iter = tokenize(contents).map(|t| {
+        let range = offset..offset + t.len;
+        offset = range.end;
+        (t.kind, &contents[range])
+    });
+
+    while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_clippy_lint") {
+        let mut iter = iter
+            .by_ref()
+            .filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
+        // matches `!{`
+        match_tokens!(iter, Bang OpenBrace);
+        match iter.next() {
+            // #[clippy::version = "version"] pub
+            Some((TokenKind::Pound, _)) => {
+                match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
+            },
+            // pub
+            Some((TokenKind::Ident, _)) => (),
+            _ => continue,
+        }
+        let (name, group, desc) = match_tokens!(
+            iter,
+            // LINT_NAME
+            Ident(name) Comma
+            // group,
+            Ident(group) Comma
+            // "description" }
+            Literal{..}(desc) CloseBrace
+        );
+        lints.push(Lint::new(name, group, desc, module));
+    }
 }
 
-/// Collects all .rs files in the `clippy_lints/src` directory
-fn lint_files() -> impl Iterator<Item = walkdir::DirEntry> {
-    // We use `WalkDir` instead of `fs::read_dir` here in order to recurse into subdirectories.
-    // Otherwise we would not collect all the lints, for example in `clippy_lints/src/methods/`.
-    let path = clippy_project_root().join("clippy_lints/src");
-    WalkDir::new(path)
-        .into_iter()
-        .filter_map(Result::ok)
-        .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
+/// Parse a source file looking for `declare_deprecated_lint` macro invocations.
+fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) {
+    let mut offset = 0usize;
+    let mut iter = tokenize(contents).map(|t| {
+        let range = offset..offset + t.len;
+        offset = range.end;
+        (t.kind, &contents[range])
+    });
+    while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_deprecated_lint") {
+        let mut iter = iter
+            .by_ref()
+            .filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
+        let (name, reason) = match_tokens!(
+            iter,
+            // !{
+            Bang OpenBrace
+            // #[clippy::version = "version"]
+            Pound OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket
+            // pub LINT_NAME,
+            Ident Ident(name) Comma
+            // "description"
+            Literal{kind: LiteralKind::Str{..},..}(reason)
+            // }
+            CloseBrace
+        );
+        lints.push(DeprecatedLint::new(name, reason));
+    }
 }
 
-/// Whether a file has had its text changed or not
-#[derive(PartialEq, Debug)]
-struct FileChange {
-    changed: bool,
-    new_lines: String,
+/// Removes the line splices and surrounding quotes from a string literal
+fn remove_line_splices(s: &str) -> String {
+    let s = s
+        .strip_prefix('r')
+        .unwrap_or(s)
+        .trim_matches('#')
+        .strip_prefix('"')
+        .and_then(|s| s.strip_suffix('"'))
+        .unwrap_or_else(|| panic!("expected quoted string, found `{}`", s));
+    let mut res = String::with_capacity(s.len());
+    unescape::unescape_literal(s, unescape::Mode::Str, &mut |range, _| res.push_str(&s[range]));
+    res
 }
 
 /// Replaces a region in a file delimited by two lines matching regexes.
@@ -396,144 +417,49 @@ struct FileChange {
 /// # Panics
 ///
 /// Panics if the path could not read or then written
-fn replace_region_in_file<F>(
+fn replace_region_in_file(
+    update_mode: UpdateMode,
     path: &Path,
     start: &str,
     end: &str,
-    replace_start: bool,
-    write_back: bool,
-    replacements: F,
-) -> FileChange
-where
-    F: FnOnce() -> Vec<String>,
-{
-    let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from {}: {}", path.display(), e));
-    let file_change = replace_region_in_text(&contents, start, end, replace_start, replacements);
-
-    if write_back {
-        if let Err(e) = fs::write(path, file_change.new_lines.as_bytes()) {
-            panic!("Cannot write to {}: {}", path.display(), e);
-        }
-    }
-    file_change
-}
-
-/// Replaces a region in a text delimited by two lines matching regexes.
-///
-/// * `text` is the input text on which you want to perform the replacement
-/// * `start` is a `&str` that describes the delimiter line before the region you want to replace.
-///   As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
-/// * `end` is a `&str` that describes the delimiter line until where the replacement should happen.
-///   As the `&str` will be converted to a `Regex`, this can contain regex syntax, too.
-/// * If `replace_start` is true, the `start` delimiter line is replaced as well. The `end`
-///   delimiter line is never replaced.
-/// * `replacements` is a closure that has to return a `Vec<String>` which contains the new text.
-///
-/// If you want to perform the replacement on files instead of already parsed text,
-/// use `replace_region_in_file`.
-///
-/// # Example
-///
-/// ```ignore
-/// let the_text = "replace_start\nsome text\nthat will be replaced\nreplace_end";
-/// let result =
-///     replace_region_in_text(the_text, "replace_start", "replace_end", false, || {
-///         vec!["a different".to_string(), "text".to_string()]
-///     })
-///     .new_lines;
-/// assert_eq!("replace_start\na different\ntext\nreplace_end", result);
-/// ```
-///
-/// # Panics
-///
-/// Panics if start or end is not valid regex
-fn replace_region_in_text<F>(text: &str, start: &str, end: &str, replace_start: bool, replacements: F) -> FileChange
-where
-    F: FnOnce() -> Vec<String>,
-{
-    let replace_it = replacements();
-    let mut in_old_region = false;
-    let mut found = false;
-    let mut new_lines = vec![];
-    let start = Regex::new(start).unwrap();
-    let end = Regex::new(end).unwrap();
-
-    for line in text.lines() {
-        if in_old_region {
-            if end.is_match(line) {
-                in_old_region = false;
-                new_lines.extend(replace_it.clone());
-                new_lines.push(line.to_string());
-            }
-        } else if start.is_match(line) {
-            if !replace_start {
-                new_lines.push(line.to_string());
+    write_replacement: impl FnMut(&mut String),
+) {
+    let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {}", path.display(), e));
+    let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) {
+        Ok(x) => x,
+        Err(delim) => panic!("Couldn't find `{}` in file `{}`", delim, path.display()),
+    };
+
+    match update_mode {
+        UpdateMode::Check if contents != new_contents => exit_with_failure(),
+        UpdateMode::Check => (),
+        UpdateMode::Change => {
+            if let Err(e) = fs::write(path, new_contents.as_bytes()) {
+                panic!("Cannot write to `{}`: {}", path.display(), e);
             }
-            in_old_region = true;
-            found = true;
-        } else {
-            new_lines.push(line.to_string());
-        }
-    }
-
-    if !found {
-        // This happens if the provided regex in `clippy_dev/src/main.rs` does not match in the
-        // given text or file. Most likely this is an error on the programmer's side and the Regex
-        // is incorrect.
-        eprintln!("error: regex \n{:?}\ndoesn't match. You may have to update it.", start);
-        std::process::exit(1);
-    }
-
-    let mut new_lines = new_lines.join("\n");
-    if text.ends_with('\n') {
-        new_lines.push('\n');
+        },
     }
-    let changed = new_lines != text;
-    FileChange { changed, new_lines }
-}
-
-#[test]
-fn test_parse_contents() {
-    let result: Vec<Lint> = parse_contents(
-        r#"
-declare_clippy_lint! {
-    #[clippy::version = "Hello Clippy!"]
-    pub PTR_ARG,
-    style,
-    "really long \
-     text"
 }
 
-declare_clippy_lint!{
-    #[clippy::version = "Test version"]
-    pub DOC_MARKDOWN,
-    pedantic,
-    "single line"
-}
-
-/// some doc comment
-declare_deprecated_lint! {
-    #[clippy::version = "I'm a version"]
-    pub SHOULD_ASSERT_EQ,
-    "`assert!()` will be more flexible with RFC 2011"
-}
-    "#,
-        "module_name",
-    )
-    .collect();
-
-    let expected = vec![
-        Lint::new("ptr_arg", "style", "really long text", None, "module_name"),
-        Lint::new("doc_markdown", "pedantic", "single line", None, "module_name"),
-        Lint::new(
-            "should_assert_eq",
-            "Deprecated",
-            "`assert!()` will be more flexible with RFC 2011",
-            Some("`assert!()` will be more flexible with RFC 2011"),
-            "module_name",
-        ),
-    ];
-    assert_eq!(expected, result);
+/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters
+/// were found, or the missing delimiter if not.
+fn replace_region_in_text<'a>(
+    text: &str,
+    start: &'a str,
+    end: &'a str,
+    mut write_replacement: impl FnMut(&mut String),
+) -> Result<String, &'a str> {
+    let (text_start, rest) = text.split_once(start).ok_or(start)?;
+    let (_, text_end) = rest.split_once(end).ok_or(end)?;
+
+    let mut res = String::with_capacity(text.len() + 4096);
+    res.push_str(text_start);
+    res.push_str(start);
+    write_replacement(&mut res);
+    res.push_str(end);
+    res.push_str(text_end);
+
+    Ok(res)
 }
 
 #[cfg(test)]
@@ -541,55 +467,65 @@ mod tests {
     use super::*;
 
     #[test]
-    fn test_replace_region() {
-        let text = "\nabc\n123\n789\ndef\nghi";
-        let expected = FileChange {
-            changed: true,
-            new_lines: "\nabc\nhello world\ndef\nghi".to_string(),
-        };
-        let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, false, || {
-            vec!["hello world".to_string()]
-        });
-        assert_eq!(expected, result);
-    }
+    fn test_parse_contents() {
+        static CONTENTS: &str = r#"
+            declare_clippy_lint! {
+                #[clippy::version = "Hello Clippy!"]
+                pub PTR_ARG,
+                style,
+                "really long \
+                text"
+            }
 
-    #[test]
-    fn test_replace_region_with_start() {
-        let text = "\nabc\n123\n789\ndef\nghi";
-        let expected = FileChange {
-            changed: true,
-            new_lines: "\nhello world\ndef\nghi".to_string(),
-        };
-        let result = replace_region_in_text(text, r#"^\s*abc$"#, r#"^\s*def"#, true, || {
-            vec!["hello world".to_string()]
-        });
+            declare_clippy_lint!{
+                #[clippy::version = "Test version"]
+                pub DOC_MARKDOWN,
+                pedantic,
+                "single line"
+            }
+        "#;
+        let mut result = Vec::new();
+        parse_contents(CONTENTS, "module_name", &mut result);
+
+        let expected = vec![
+            Lint::new("ptr_arg", "style", "\"really long text\"", "module_name"),
+            Lint::new("doc_markdown", "pedantic", "\"single line\"", "module_name"),
+        ];
         assert_eq!(expected, result);
     }
 
     #[test]
-    fn test_replace_region_no_changes() {
-        let text = "123\n456\n789";
-        let expected = FileChange {
-            changed: false,
-            new_lines: "123\n456\n789".to_string(),
-        };
-        let result = replace_region_in_text(text, r#"^\s*123$"#, r#"^\s*456"#, false, Vec::new);
+    fn test_parse_deprecated_contents() {
+        static DEPRECATED_CONTENTS: &str = r#"
+            /// some doc comment
+            declare_deprecated_lint! {
+                #[clippy::version = "I'm a version"]
+                pub SHOULD_ASSERT_EQ,
+                "`assert!()` will be more flexible with RFC 2011"
+            }
+        "#;
+
+        let mut result = Vec::new();
+        parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result);
+
+        let expected = vec![DeprecatedLint::new(
+            "should_assert_eq",
+            "\"`assert!()` will be more flexible with RFC 2011\"",
+        )];
         assert_eq!(expected, result);
     }
 
     #[test]
     fn test_usable_lints() {
         let lints = vec![
-            Lint::new("should_assert_eq", "Deprecated", "abc", Some("Reason"), "module_name"),
-            Lint::new("should_assert_eq2", "Not Deprecated", "abc", None, "module_name"),
-            Lint::new("should_assert_eq2", "internal", "abc", None, "module_name"),
-            Lint::new("should_assert_eq2", "internal_style", "abc", None, "module_name"),
+            Lint::new("should_assert_eq2", "Not Deprecated", "\"abc\"", "module_name"),
+            Lint::new("should_assert_eq2", "internal", "\"abc\"", "module_name"),
+            Lint::new("should_assert_eq2", "internal_style", "\"abc\"", "module_name"),
         ];
         let expected = vec![Lint::new(
             "should_assert_eq2",
             "Not Deprecated",
-            "abc",
-            None,
+            "\"abc\"",
             "module_name",
         )];
         assert_eq!(expected, Lint::usable_lints(&lints));
@@ -598,55 +534,30 @@ fn test_usable_lints() {
     #[test]
     fn test_by_lint_group() {
         let lints = vec![
-            Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
-            Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
-            Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
+            Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
+            Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name"),
+            Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"),
         ];
         let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
         expected.insert(
             "group1".to_string(),
             vec![
-                Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
-                Lint::new("incorrect_match", "group1", "abc", None, "module_name"),
+                Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
+                Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"),
             ],
         );
         expected.insert(
             "group2".to_string(),
-            vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")],
+            vec![Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name")],
         );
         assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
     }
 
-    #[test]
-    fn test_gen_changelog_lint_list() {
-        let lints = vec![
-            Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
-            Lint::new("should_assert_eq2", "group2", "abc", None, "module_name"),
-        ];
-        let expected = vec![
-            format!("[`should_assert_eq`]: {}#should_assert_eq", DOCS_LINK),
-            format!("[`should_assert_eq2`]: {}#should_assert_eq2", DOCS_LINK),
-        ];
-        assert_eq!(expected, gen_changelog_lint_list(lints.iter()));
-    }
-
     #[test]
     fn test_gen_deprecated() {
         let lints = vec![
-            Lint::new(
-                "should_assert_eq",
-                "group1",
-                "abc",
-                Some("has been superseded by should_assert_eq2"),
-                "module_name",
-            ),
-            Lint::new(
-                "another_deprecated",
-                "group2",
-                "abc",
-                Some("will be removed"),
-                "module_name",
-            ),
+            DeprecatedLint::new("should_assert_eq", "\"has been superseded by should_assert_eq2\""),
+            DeprecatedLint::new("another_deprecated", "\"will be removed\""),
         ];
 
         let expected = GENERATED_FILE_COMMENT.to_string()
@@ -665,32 +576,15 @@ fn test_gen_deprecated() {
             .join("\n")
             + "\n";
 
-        assert_eq!(expected, gen_deprecated(lints.iter()));
-    }
-
-    #[test]
-    #[should_panic]
-    fn test_gen_deprecated_fail() {
-        let lints = vec![Lint::new("should_assert_eq2", "group2", "abc", None, "module_name")];
-        let _deprecated_lints = gen_deprecated(lints.iter());
-    }
-
-    #[test]
-    fn test_gen_modules_list() {
-        let lints = vec![
-            Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
-            Lint::new("incorrect_stuff", "group3", "abc", None, "another_module"),
-        ];
-        let expected = vec!["mod another_module;".to_string(), "mod module_name;".to_string()];
-        assert_eq!(expected, gen_modules_list(lints.iter()));
+        assert_eq!(expected, gen_deprecated(&lints));
     }
 
     #[test]
     fn test_gen_lint_group_list() {
         let lints = vec![
-            Lint::new("abc", "group1", "abc", None, "module_name"),
-            Lint::new("should_assert_eq", "group1", "abc", None, "module_name"),
-            Lint::new("internal", "internal_style", "abc", None, "module_name"),
+            Lint::new("abc", "group1", "\"abc\"", "module_name"),
+            Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
+            Lint::new("internal", "internal_style", "\"abc\"", "module_name"),
         ];
         let expected = GENERATED_FILE_COMMENT.to_string()
             + &[
index 66e61660d313aa150e1fcbb9106fce4d83bc7ffe..aebf9a87cabd2c7369816bbfcb7386eea0cba6fb 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_lints"
-version = "0.1.61"
+version = "0.1.62"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
diff --git a/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/clippy_lints/src/casts/cast_abs_to_unsigned.rs
new file mode 100644 (file)
index 0000000..e9b0f1f
--- /dev/null
@@ -0,0 +1,42 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{meets_msrv, msrvs};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::Ty;
+use rustc_semver::RustcVersion;
+
+use super::CAST_ABS_TO_UNSIGNED;
+
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    cast_expr: &Expr<'_>,
+    cast_from: Ty<'_>,
+    cast_to: Ty<'_>,
+    msrv: &Option<RustcVersion>,
+) {
+    if_chain! {
+        if meets_msrv(msrv.as_ref(), &msrvs::UNSIGNED_ABS);
+        if cast_from.is_integral();
+        if cast_to.is_integral();
+        if cast_from.is_signed();
+        if !cast_to.is_signed();
+        if let ExprKind::MethodCall(method_path, args, _) = cast_expr.kind;
+        if let method_name = method_path.ident.name.as_str();
+        if method_name == "abs";
+        then {
+            span_lint_and_sugg(
+                cx,
+                CAST_ABS_TO_UNSIGNED,
+                expr.span,
+                &format!("casting the result of `{}::{}()` to {}", cast_from, method_name, cast_to),
+                "replace with",
+                format!("{}.unsigned_abs()", Sugg::hir(cx, &args[0], "..")),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
index a4ef1344ab9511beb75790081393d1e456c98040..d476a1a7646c01e24bd8ea9b3df3297ec980fdba 100644 (file)
@@ -1,7 +1,6 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::is_hir_ty_cfg_dependant;
 use clippy_utils::ty::is_c_void;
-use if_chain::if_chain;
+use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, match_any_def_paths, paths};
 use rustc_hir::{Expr, ExprKind, GenericArg};
 use rustc_lint::LateContext;
 use rustc_middle::ty::layout::LayoutOf;
@@ -20,45 +19,78 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
         );
         lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
     } else if let ExprKind::MethodCall(method_path, [self_arg, ..], _) = &expr.kind {
-        if_chain! {
-            if method_path.ident.name == sym!(cast);
-            if let Some(generic_args) = method_path.args;
-            if let [GenericArg::Type(cast_to)] = generic_args.args;
+        if method_path.ident.name == sym!(cast)
+            && let Some(generic_args) = method_path.args
+            && let [GenericArg::Type(cast_to)] = generic_args.args
             // There probably is no obvious reason to do this, just to be consistent with `as` cases.
-            if !is_hir_ty_cfg_dependant(cx, cast_to);
-            then {
-                let (cast_from, cast_to) =
-                    (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
-                lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
-            }
+            && !is_hir_ty_cfg_dependant(cx, cast_to)
+        {
+            let (cast_from, cast_to) =
+                (cx.typeck_results().expr_ty(self_arg), cx.typeck_results().expr_ty(expr));
+            lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
         }
     }
 }
 
 fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) {
-    if_chain! {
-        if let ty::RawPtr(from_ptr_ty) = &cast_from.kind();
-        if let ty::RawPtr(to_ptr_ty) = &cast_to.kind();
-        if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty);
-        if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty);
-        if from_layout.align.abi < to_layout.align.abi;
+    if let ty::RawPtr(from_ptr_ty) = &cast_from.kind()
+        && let ty::RawPtr(to_ptr_ty) = &cast_to.kind()
+        && let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty)
+        && let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty)
+        && from_layout.align.abi < to_layout.align.abi
         // with c_void, we inherently need to trust the user
-        if !is_c_void(cx, from_ptr_ty.ty);
+        && !is_c_void(cx, from_ptr_ty.ty)
         // when casting from a ZST, we don't know enough to properly lint
-        if !from_layout.is_zst();
-        then {
-            span_lint(
-                cx,
-                CAST_PTR_ALIGNMENT,
-                expr.span,
-                &format!(
-                    "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)",
-                    cast_from,
-                    cast_to,
-                    from_layout.align.abi.bytes(),
-                    to_layout.align.abi.bytes(),
-                ),
-            );
-        }
+        && !from_layout.is_zst()
+        && !is_used_as_unaligned(cx, expr)
+    {
+        span_lint(
+            cx,
+            CAST_PTR_ALIGNMENT,
+            expr.span,
+            &format!(
+                "casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)",
+                cast_from,
+                cast_to,
+                from_layout.align.abi.bytes(),
+                to_layout.align.abi.bytes(),
+            ),
+        );
+    }
+}
+
+fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+    let Some(parent) = get_parent_expr(cx, e) else {
+        return false;
+    };
+    match parent.kind {
+        ExprKind::MethodCall(name, [self_arg, ..], _) if self_arg.hir_id == e.hir_id => {
+            if matches!(name.ident.as_str(), "read_unaligned" | "write_unaligned")
+                && let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent.hir_id)
+                && let Some(def_id) = cx.tcx.impl_of_method(def_id)
+                && cx.tcx.type_of(def_id).is_unsafe_ptr()
+            {
+                true
+            } else {
+                false
+            }
+        },
+        ExprKind::Call(func, [arg, ..]) if arg.hir_id == e.hir_id => {
+            static PATHS: &[&[&str]] = &[
+                paths::PTR_READ_UNALIGNED.as_slice(),
+                paths::PTR_WRITE_UNALIGNED.as_slice(),
+                paths::PTR_UNALIGNED_VOLATILE_LOAD.as_slice(),
+                paths::PTR_UNALIGNED_VOLATILE_STORE.as_slice(),
+            ];
+            if let ExprKind::Path(path) = &func.kind
+                && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
+                && match_any_def_paths(cx, def_id, PATHS).is_some()
+            {
+                true
+            } else {
+                false
+            }
+        },
+        _ => false,
     }
 }
index be59145afa0032a3cd7c8882c770dd48f4a53288..55c1f085657bb1842eafd7c52ff5e75a5b68321c 100644 (file)
@@ -1,3 +1,4 @@
+mod cast_abs_to_unsigned;
 mod cast_enum_constructor;
 mod cast_lossless;
 mod cast_possible_truncation;
     "casts from an enum tuple constructor to an integer"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for uses of the `abs()` method that cast the result to unsigned.
+    ///
+    /// ### Why is this bad?
+    /// The `unsigned_abs()` method avoids panic when called on the MIN value.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x: i32 = -42;
+    /// let y: u32 = x.abs() as u32;
+    /// ```
+    /// Use instead:
+    /// let x: i32 = -42;
+    /// let y: u32 = x.unsigned_abs();
+    /// ```
+    #[clippy::version = "1.61.0"]
+    pub CAST_ABS_TO_UNSIGNED,
+    suspicious,
+    "casting the result of `abs()` to an unsigned integer can panic"
+}
+
 pub struct Casts {
     msrv: Option<RustcVersion>,
 }
@@ -500,7 +523,8 @@ pub fn new(msrv: Option<RustcVersion>) -> Self {
     CHAR_LIT_AS_U8,
     PTR_AS_PTR,
     CAST_ENUM_TRUNCATION,
-    CAST_ENUM_CONSTRUCTOR
+    CAST_ENUM_CONSTRUCTOR,
+    CAST_ABS_TO_UNSIGNED
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -536,6 +560,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     cast_possible_wrap::check(cx, expr, cast_from, cast_to);
                     cast_precision_loss::check(cx, expr, cast_from, cast_to);
                     cast_sign_loss::check(cx, expr, cast_expr, cast_from, cast_to);
+                    cast_abs_to_unsigned::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
                 }
                 cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to, &self.msrv);
                 cast_enum_constructor::check(cx, expr, cast_expr, cast_from);
index 470c8c7ea26a60dd2b48b142ffac3cb6ec10a63b..af56ec11ef8acd741ec4a8a45926d8fd2656e36a 100644 (file)
@@ -4,7 +4,8 @@
 use if_chain::if_chain;
 use rustc_ast::{LitFloatType, LitIntType, LitKind};
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, Lit, UnOp};
+use rustc_hir::def::Res;
+use rustc_hir::{Expr, ExprKind, Lit, QPath, TyKind, UnOp};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, FloatTy, InferTy, Ty};
@@ -18,6 +19,17 @@ pub(super) fn check(
     cast_from: Ty<'_>,
     cast_to: Ty<'_>,
 ) -> bool {
+    // skip non-primitive type cast
+    if_chain! {
+        if let ExprKind::Cast(_, cast_to) = expr.kind;
+        if let TyKind::Path(QPath::Resolved(_, path)) = &cast_to.kind;
+        if let Res::PrimTy(_) = path.res;
+        then {}
+        else {
+            return false
+        }
+    }
+
     if let Some(lit) = get_numeric_literal(cast_expr) {
         let literal_str = snippet_opt(cx, cast_expr.span).unwrap_or_default();
 
diff --git a/clippy_lints/src/crate_in_macro_def.rs b/clippy_lints/src/crate_in_macro_def.rs
new file mode 100644 (file)
index 0000000..fc141b4
--- /dev/null
@@ -0,0 +1,125 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use rustc_ast::ast::{AttrKind, Attribute, Item, ItemKind};
+use rustc_ast::token::{Token, TokenKind};
+use rustc_ast::tokenstream::{TokenStream, TokenTree};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{symbol::sym, Span};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for use of `crate` as opposed to `$crate` in a macro definition.
+    ///
+    /// ### Why is this bad?
+    /// `crate` refers to the macro call's crate, whereas `$crate` refers to the macro definition's
+    /// crate. Rarely is the former intended. See:
+    /// https://doc.rust-lang.org/reference/macros-by-example.html#hygiene
+    ///
+    /// ### Example
+    /// ```rust
+    /// #[macro_export]
+    /// macro_rules! print_message {
+    ///     () => {
+    ///         println!("{}", crate::MESSAGE);
+    ///     };
+    /// }
+    /// pub const MESSAGE: &str = "Hello!";
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// #[macro_export]
+    /// macro_rules! print_message {
+    ///     () => {
+    ///         println!("{}", $crate::MESSAGE);
+    ///     };
+    /// }
+    /// pub const MESSAGE: &str = "Hello!";
+    /// ```
+    ///
+    /// Note that if the use of `crate` is intentional, an `allow` attribute can be applied to the
+    /// macro definition, e.g.:
+    /// ```rust,ignore
+    /// #[allow(clippy::crate_in_macro_def)]
+    /// macro_rules! ok { ... crate::foo ... }
+    /// ```
+    #[clippy::version = "1.61.0"]
+    pub CRATE_IN_MACRO_DEF,
+    suspicious,
+    "using `crate` in a macro definition"
+}
+declare_lint_pass!(CrateInMacroDef => [CRATE_IN_MACRO_DEF]);
+
+impl EarlyLintPass for CrateInMacroDef {
+    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+        if_chain! {
+            if item.attrs.iter().any(is_macro_export);
+            if let ItemKind::MacroDef(macro_def) = &item.kind;
+            let tts = macro_def.body.inner_tokens();
+            if let Some(span) = contains_unhygienic_crate_reference(&tts);
+            then {
+                span_lint_and_sugg(
+                    cx,
+                    CRATE_IN_MACRO_DEF,
+                    span,
+                    "`crate` references the macro call's crate",
+                    "to reference the macro definition's crate, use",
+                    String::from("$crate"),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+    }
+}
+
+fn is_macro_export(attr: &Attribute) -> bool {
+    if_chain! {
+        if let AttrKind::Normal(attr_item, _) = &attr.kind;
+        if let [segment] = attr_item.path.segments.as_slice();
+        then {
+            segment.ident.name == sym::macro_export
+        } else {
+            false
+        }
+    }
+}
+
+fn contains_unhygienic_crate_reference(tts: &TokenStream) -> Option<Span> {
+    let mut prev_is_dollar = false;
+    let mut cursor = tts.trees();
+    while let Some(curr) = cursor.next() {
+        if_chain! {
+            if !prev_is_dollar;
+            if let Some(span) = is_crate_keyword(&curr);
+            if let Some(next) = cursor.look_ahead(0);
+            if is_token(next, &TokenKind::ModSep);
+            then {
+                return Some(span);
+            }
+        }
+        if let TokenTree::Delimited(_, _, tts) = &curr {
+            let span = contains_unhygienic_crate_reference(tts);
+            if span.is_some() {
+                return span;
+            }
+        }
+        prev_is_dollar = is_token(&curr, &TokenKind::Dollar);
+    }
+    None
+}
+
+fn is_crate_keyword(tt: &TokenTree) -> Option<Span> {
+    if_chain! {
+        if let TokenTree::Token(Token { kind: TokenKind::Ident(symbol, _), span }) = tt;
+        if symbol.as_str() == "crate";
+        then { Some(*span) } else { None }
+    }
+}
+
+fn is_token(tt: &TokenTree, kind: &TokenKind) -> bool {
+    if let TokenTree::Token(Token { kind: other, .. }) = tt {
+        kind == other
+    } else {
+        false
+    }
+}
index 92cf82bcd6a34bf314df7c35824f6c4e8b7189ff..28d0c75fde6baf95b8d89bc22f973ae928509072 100644 (file)
@@ -621,8 +621,8 @@ fn has_needless_main(code: String, edition: Edition) -> bool {
                 let filename = FileName::anon_source_code(&code);
 
                 let sm = Lrc::new(SourceMap::new(FilePathMapping::empty()));
-                let fallback_bundle = rustc_errors::fallback_fluent_bundle(false)
-                    .expect("failed to load fallback fluent bundle");
+                let fallback_bundle =
+                    rustc_errors::fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle");
                 let emitter = EmitterWriter::new(
                     Box::new(io::sink()),
                     None,
index 5c4b35fd4b9d2e572b155fb29e85b3cf7019ecf3..88c54828da834da3e94ae9d513a76102e1d1f0c9 100644 (file)
@@ -1,9 +1,8 @@
-use clippy_utils::diagnostics::span_lint_and_note;
-use clippy_utils::ty::is_copy;
-use if_chain::if_chain;
-use rustc_hir::{Expr, ExprKind};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
+use clippy_utils::is_must_use_func_call;
+use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item};
+use rustc_hir::{Expr, ExprKind, LangItem};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
 
     "calls to `std::mem::forget` with a value that implements Copy"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for calls to `std::mem::drop` with a value that does not implement `Drop`.
+    ///
+    /// ### Why is this bad?
+    /// Calling `std::mem::drop` is no different than dropping such a type. A different value may
+    /// have been intended.
+    ///
+    /// ### Example
+    /// ```rust
+    /// struct Foo;
+    /// let x = Foo;
+    /// std::mem::drop(x);
+    /// ```
+    #[clippy::version = "1.61.0"]
+    pub DROP_NON_DROP,
+    suspicious,
+    "call to `std::mem::drop` with a value which does not implement `Drop`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for calls to `std::mem::forget` with a value that does not implement `Drop`.
+    ///
+    /// ### Why is this bad?
+    /// Calling `std::mem::forget` is no different than dropping such a type. A different value may
+    /// have been intended.
+    ///
+    /// ### Example
+    /// ```rust
+    /// struct Foo;
+    /// let x = Foo;
+    /// std::mem::forget(x);
+    /// ```
+    #[clippy::version = "1.61.0"]
+    pub FORGET_NON_DROP,
+    suspicious,
+    "call to `std::mem::forget` with a value which does not implement `Drop`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.
+    ///
+    /// ### Why is this bad?
+    /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`.
+    ///
+    /// ### Known problems
+    /// Does not catch cases if the user binds `std::mem::drop`
+    /// to a different name and calls it that way.
+    ///
+    /// ### Example
+    /// ```rust
+    /// struct S;
+    /// drop(std::mem::ManuallyDrop::new(S));
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// struct S;
+    /// unsafe {
+    ///     std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
+    /// }
+    /// ```
+    #[clippy::version = "1.49.0"]
+    pub UNDROPPED_MANUALLY_DROPS,
+    correctness,
+    "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value"
+}
+
 const DROP_REF_SUMMARY: &str = "calls to `std::mem::drop` with a reference instead of an owned value. \
                                 Dropping a reference does nothing";
 const FORGET_REF_SUMMARY: &str = "calls to `std::mem::forget` with a reference instead of an owned value. \
                                  Dropping a copy leaves the original intact";
 const FORGET_COPY_SUMMARY: &str = "calls to `std::mem::forget` with a value that implements `Copy`. \
                                    Forgetting a copy leaves the original intact";
+const DROP_NON_DROP_SUMMARY: &str = "call to `std::mem::drop` with a value that does not implement `Drop`. \
+                                 Dropping such a type only extends it's contained lifetimes";
+const FORGET_NON_DROP_SUMMARY: &str = "call to `std::mem::forget` with a value that does not implement `Drop`. \
+                                   Forgetting such a type is the same as dropping it";
 
-declare_lint_pass!(DropForgetRef => [DROP_REF, FORGET_REF, DROP_COPY, FORGET_COPY]);
+declare_lint_pass!(DropForgetRef => [
+    DROP_REF,
+    FORGET_REF,
+    DROP_COPY,
+    FORGET_COPY,
+    DROP_NON_DROP,
+    FORGET_NON_DROP,
+    UNDROPPED_MANUALLY_DROPS
+]);
 
 impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            if let ExprKind::Call(path, args) = expr.kind;
-            if let ExprKind::Path(ref qpath) = path.kind;
-            if args.len() == 1;
-            if let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id();
-            then {
-                let lint;
-                let msg;
-                let arg = &args[0];
-                let arg_ty = cx.typeck_results().expr_ty(arg);
-
-                if let ty::Ref(..) = arg_ty.kind() {
-                    match cx.tcx.get_diagnostic_name(def_id) {
-                        Some(sym::mem_drop) => {
-                            lint = DROP_REF;
-                            msg = DROP_REF_SUMMARY.to_string();
-                        },
-                        Some(sym::mem_forget) => {
-                            lint = FORGET_REF;
-                            msg = FORGET_REF_SUMMARY.to_string();
-                        },
-                        _ => return,
-                    }
-                    span_lint_and_note(cx,
-                                       lint,
-                                       expr.span,
-                                       &msg,
-                                       Some(arg.span),
-                                       &format!("argument has type `{}`", arg_ty));
-                } else if is_copy(cx, arg_ty) {
-                    match cx.tcx.get_diagnostic_name(def_id) {
-                        Some(sym::mem_drop) => {
-                            lint = DROP_COPY;
-                            msg = DROP_COPY_SUMMARY.to_string();
-                        },
-                        Some(sym::mem_forget) => {
-                            lint = FORGET_COPY;
-                            msg = FORGET_COPY_SUMMARY.to_string();
-                        },
-                        _ => return,
-                    }
-                    span_lint_and_note(cx,
-                                       lint,
-                                       expr.span,
-                                       &msg,
-                                       Some(arg.span),
-                                       &format!("argument has type {}", arg_ty));
+        if let ExprKind::Call(path, [arg]) = expr.kind
+            && let ExprKind::Path(ref qpath) = path.kind
+            && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
+            && let Some(fn_name) = cx.tcx.get_diagnostic_name(def_id)
+        {
+            let arg_ty = cx.typeck_results().expr_ty(arg);
+            let (lint, msg) = match fn_name {
+                sym::mem_drop if arg_ty.is_ref() => (DROP_REF, DROP_REF_SUMMARY),
+                sym::mem_forget if arg_ty.is_ref() => (FORGET_REF, FORGET_REF_SUMMARY),
+                sym::mem_drop if is_copy(cx, arg_ty) => (DROP_COPY, DROP_COPY_SUMMARY),
+                sym::mem_forget if is_copy(cx, arg_ty) => (FORGET_COPY, FORGET_COPY_SUMMARY),
+                sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => {
+                    span_lint_and_help(
+                        cx,
+                        UNDROPPED_MANUALLY_DROPS,
+                        expr.span,
+                        "the inner value of this ManuallyDrop will not be dropped",
+                        None,
+                        "to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop",
+                    );
+                    return;
                 }
-            }
+                sym::mem_drop
+                    if !(arg_ty.needs_drop(cx.tcx, cx.param_env)
+                        || is_must_use_func_call(cx, arg)
+                        || is_must_use_ty(cx, arg_ty)) =>
+                {
+                    (DROP_NON_DROP, DROP_NON_DROP_SUMMARY)
+                },
+                sym::mem_forget if !arg_ty.needs_drop(cx.tcx, cx.param_env) => {
+                    (FORGET_NON_DROP, FORGET_NON_DROP_SUMMARY)
+                },
+                _ => return,
+            };
+            span_lint_and_note(
+                cx,
+                lint,
+                expr.span,
+                msg,
+                Some(arg.span),
+                &format!("argument has type `{}`", arg_ty),
+            );
         }
     }
 }
diff --git a/clippy_lints/src/empty_structs_with_brackets.rs b/clippy_lints/src/empty_structs_with_brackets.rs
new file mode 100644 (file)
index 0000000..fdeac8d
--- /dev/null
@@ -0,0 +1,99 @@
+use clippy_utils::{diagnostics::span_lint_and_then, source::snippet_opt};
+use rustc_ast::ast::{Item, ItemKind, VariantData};
+use rustc_errors::Applicability;
+use rustc_lexer::TokenKind;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Finds structs without fields (a so-called "empty struct") that are declared with brackets.
+    ///
+    /// ### Why is this bad?
+    /// Empty brackets after a struct declaration can be omitted.
+    ///
+    /// ### Example
+    /// ```rust
+    /// struct Cookie {}
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// struct Cookie;
+    /// ```
+    #[clippy::version = "1.62.0"]
+    pub EMPTY_STRUCTS_WITH_BRACKETS,
+    restriction,
+    "finds struct declarations with empty brackets"
+}
+declare_lint_pass!(EmptyStructsWithBrackets => [EMPTY_STRUCTS_WITH_BRACKETS]);
+
+impl EarlyLintPass for EmptyStructsWithBrackets {
+    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
+        let span_after_ident = item.span.with_lo(item.ident.span.hi());
+
+        if let ItemKind::Struct(var_data, _) = &item.kind
+            && has_brackets(var_data)
+            && has_no_fields(cx, var_data, span_after_ident) {
+            span_lint_and_then(
+                cx,
+                EMPTY_STRUCTS_WITH_BRACKETS,
+                span_after_ident,
+                "found empty brackets on struct declaration",
+                |diagnostic| {
+                    diagnostic.span_suggestion_hidden(
+                        span_after_ident,
+                        "remove the brackets",
+                        ";".to_string(),
+                        Applicability::MachineApplicable);
+                    },
+            );
+        }
+    }
+}
+
+fn has_no_ident_token(braces_span_str: &str) -> bool {
+    !rustc_lexer::tokenize(braces_span_str).any(|t| t.kind == TokenKind::Ident)
+}
+
+fn has_brackets(var_data: &VariantData) -> bool {
+    !matches!(var_data, VariantData::Unit(_))
+}
+
+fn has_no_fields(cx: &EarlyContext<'_>, var_data: &VariantData, braces_span: Span) -> bool {
+    if !var_data.fields().is_empty() {
+        return false;
+    }
+
+    // there might still be field declarations hidden from the AST
+    // (conditionaly compiled code using #[cfg(..)])
+
+    let Some(braces_span_str) = snippet_opt(cx, braces_span) else {
+        return false;
+    };
+
+    has_no_ident_token(braces_span_str.as_ref())
+}
+
+#[cfg(test)]
+mod unit_test {
+    use super::*;
+
+    #[test]
+    fn test_has_no_ident_token() {
+        let input = "{ field: u8 }";
+        assert!(!has_no_ident_token(input));
+
+        let input = "(u8, String);";
+        assert!(!has_no_ident_token(input));
+
+        let input = " {
+                // test = 5
+        }
+        ";
+        assert!(has_no_ident_token(input));
+
+        let input = " ();";
+        assert!(has_no_ident_token(input));
+    }
+}
index f824f20ca40a017b28d725fcc452fc50f3209e55..4d6bef89bea7f09cbbfa8f618b4d26b871e371b6 100644 (file)
@@ -5,7 +5,7 @@
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
 
-use clippy_utils::consts::{constant_simple, Constant};
+use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt};
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::{clip, unsext};
 
@@ -54,6 +54,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                     check(cx, left, -1, e.span, right.span);
                     check(cx, right, -1, e.span, left.span);
                 },
+                BinOpKind::Rem => check_remainder(cx, left, right, e.span, left.span),
                 _ => (),
             }
         }
@@ -70,6 +71,18 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_
             && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)))
 }
 
+fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) {
+    let lhs_const = constant_full_int(cx, cx.typeck_results(), left);
+    let rhs_const = constant_full_int(cx, cx.typeck_results(), right);
+    if match (lhs_const, rhs_const) {
+        (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(),
+        (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv,
+        _ => return,
+    } {
+        span_ineffective_operation(cx, span, arg);
+    }
+}
+
 fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) {
     if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) {
         let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
@@ -83,15 +96,19 @@ fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span) {
             1 => v == 1,
             _ => unreachable!(),
         } {
-            span_lint(
-                cx,
-                IDENTITY_OP,
-                span,
-                &format!(
-                    "the operation is ineffective. Consider reducing it to `{}`",
-                    snippet(cx, arg, "..")
-                ),
-            );
+            span_ineffective_operation(cx, span, arg);
         }
     }
 }
+
+fn span_ineffective_operation(cx: &LateContext<'_>, span: Span, arg: Span) {
+    span_lint(
+        cx,
+        IDENTITY_OP,
+        span,
+        &format!(
+            "the operation is ineffective. Consider reducing it to `{}`",
+            snippet(cx, arg, "..")
+        ),
+    );
+}
index 9ead4bb27a5881eb3ab43d6def4481f658999b44..4ba7477add82a420dc3b160da1782e880adf85a0 100644 (file)
 
 impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if cx.tcx.hir().is_inside_const_context(expr.hir_id) {
+            return;
+        }
+
         if let ExprKind::Index(array, index) = &expr.kind {
             let ty = cx.typeck_results().expr_ty(array).peel_refs();
             if let Some(range) = higher::Range::hir(index) {
@@ -151,6 +155,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             } else {
                 // Catchall non-range index, i.e., [n] or [n << m]
                 if let ty::Array(..) = ty.kind() {
+                    // Index is a const block.
+                    if let ExprKind::ConstBlock(..) = index.kind {
+                        return;
+                    }
                     // Index is a constant uint.
                     if let Some(..) = constant(cx, cx.typeck_results(), index) {
                         // Let rustc's `const_err` lint handle constant `usize` indexing on arrays.
index 132a466267626e51fd6b0444e0e48763db3880d5..14ca93b5f3c14b3629a0168b421a3527797ffc8d 100644 (file)
@@ -23,6 +23,7 @@
     LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
     LintId::of(booleans::LOGIC_BUG),
     LintId::of(booleans::NONMINIMAL_BOOL),
+    LintId::of(casts::CAST_ABS_TO_UNSIGNED),
     LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
     LintId::of(casts::CAST_ENUM_TRUNCATION),
     LintId::of(casts::CAST_REF_TO_MUT),
@@ -37,6 +38,7 @@
     LintId::of(comparison_chain::COMPARISON_CHAIN),
     LintId::of(copies::IFS_SAME_COND),
     LintId::of(copies::IF_SAME_THEN_ELSE),
+    LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
     LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
     LintId::of(dereference::NEEDLESS_BORROW),
     LintId::of(derivable_impls::DERIVABLE_IMPLS),
     LintId::of(double_comparison::DOUBLE_COMPARISONS),
     LintId::of(double_parens::DOUBLE_PARENS),
     LintId::of(drop_forget_ref::DROP_COPY),
+    LintId::of(drop_forget_ref::DROP_NON_DROP),
     LintId::of(drop_forget_ref::DROP_REF),
     LintId::of(drop_forget_ref::FORGET_COPY),
+    LintId::of(drop_forget_ref::FORGET_NON_DROP),
     LintId::of(drop_forget_ref::FORGET_REF),
+    LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
     LintId::of(duration_subsec::DURATION_SUBSEC),
     LintId::of(entry::MAP_ENTRY),
     LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
     LintId::of(methods::CHARS_NEXT_CMP),
     LintId::of(methods::CLONE_DOUBLE_REF),
     LintId::of(methods::CLONE_ON_COPY),
+    LintId::of(methods::ERR_EXPECT),
     LintId::of(methods::EXPECT_FUN_CALL),
     LintId::of(methods::EXTEND_WITH_DRAIN),
     LintId::of(methods::FILTER_MAP_IDENTITY),
     LintId::of(methods::MAP_COLLECT_RESULT_UNIT),
     LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_IDENTITY),
+    LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
     LintId::of(methods::NEEDLESS_SPLITN),
     LintId::of(methods::NEW_RET_NO_SELF),
     LintId::of(methods::OK_EXPECT),
     LintId::of(needless_bool::NEEDLESS_BOOL),
     LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
     LintId::of(needless_late_init::NEEDLESS_LATE_INIT),
-    LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
     LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
     LintId::of(needless_update::NEEDLESS_UPDATE),
     LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
     LintId::of(types::REDUNDANT_ALLOCATION),
     LintId::of(types::TYPE_COMPLEXITY),
     LintId::of(types::VEC_BOX),
-    LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
     LintId::of(unicode::INVISIBLE_CHARACTERS),
     LintId::of(uninit_vec::UNINIT_VEC),
     LintId::of(unit_hash::UNIT_HASH),
index a2ce69065f94d47685a1c47f1fa8deb01ecbc05b..10369a855ae6e007f43469e7c58e4f38e4368376 100644 (file)
@@ -44,6 +44,7 @@
     LintId::of(methods::MANUAL_SPLIT_ONCE),
     LintId::of(methods::MAP_FLATTEN),
     LintId::of(methods::MAP_IDENTITY),
+    LintId::of(methods::NEEDLESS_OPTION_AS_DEREF),
     LintId::of(methods::NEEDLESS_SPLITN),
     LintId::of(methods::OPTION_AS_REF_DEREF),
     LintId::of(methods::OPTION_FILTER_MAP),
@@ -60,7 +61,6 @@
     LintId::of(needless_bool::BOOL_COMPARISON),
     LintId::of(needless_bool::NEEDLESS_BOOL),
     LintId::of(needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE),
-    LintId::of(needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF),
     LintId::of(needless_question_mark::NEEDLESS_QUESTION_MARK),
     LintId::of(needless_update::NEEDLESS_UPDATE),
     LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
index df63f84463dba27345e320748c0b8117dff9d840..6bf2c4bbaedc024ad45ac7970c6fc030be7c9d67 100644 (file)
@@ -22,6 +22,7 @@
     LintId::of(drop_forget_ref::DROP_REF),
     LintId::of(drop_forget_ref::FORGET_COPY),
     LintId::of(drop_forget_ref::FORGET_REF),
+    LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
     LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
     LintId::of(eq_op::EQ_OP),
     LintId::of(erasing_op::ERASING_OP),
@@ -62,7 +63,6 @@
     LintId::of(transmute::UNSOUND_COLLECTION_TRANSMUTE),
     LintId::of(transmute::WRONG_TRANSMUTE),
     LintId::of(transmuting_null::TRANSMUTING_NULL),
-    LintId::of(undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
     LintId::of(unicode::INVISIBLE_CHARACTERS),
     LintId::of(uninit_vec::UNINIT_VEC),
     LintId::of(unit_hash::UNIT_HASH),
index 21f1ef562b5a31482c01c3a56af620d9450e2ad4..532590aaa5a3d294d96dc5cf831318ee8a97c276 100644 (file)
@@ -70,6 +70,7 @@
     cargo::REDUNDANT_FEATURE_NAMES,
     cargo::WILDCARD_DEPENDENCIES,
     case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS,
+    casts::CAST_ABS_TO_UNSIGNED,
     casts::CAST_ENUM_CONSTRUCTOR,
     casts::CAST_ENUM_TRUNCATION,
     casts::CAST_LOSSLESS,
@@ -97,6 +98,7 @@
     copies::IF_SAME_THEN_ELSE,
     copies::SAME_FUNCTIONS_IN_IF_CONDITION,
     copy_iterator::COPY_ITERATOR,
+    crate_in_macro_def::CRATE_IN_MACRO_DEF,
     create_dir::CREATE_DIR,
     dbg_macro::DBG_MACRO,
     default::DEFAULT_TRAIT_ACCESS,
     double_comparison::DOUBLE_COMPARISONS,
     double_parens::DOUBLE_PARENS,
     drop_forget_ref::DROP_COPY,
+    drop_forget_ref::DROP_NON_DROP,
     drop_forget_ref::DROP_REF,
     drop_forget_ref::FORGET_COPY,
+    drop_forget_ref::FORGET_NON_DROP,
     drop_forget_ref::FORGET_REF,
+    drop_forget_ref::UNDROPPED_MANUALLY_DROPS,
     duration_subsec::DURATION_SUBSEC,
     else_if_without_else::ELSE_IF_WITHOUT_ELSE,
     empty_enum::EMPTY_ENUM,
+    empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS,
     entry::MAP_ENTRY,
     enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
     enum_variants::ENUM_VARIANT_NAMES,
     methods::CLONE_DOUBLE_REF,
     methods::CLONE_ON_COPY,
     methods::CLONE_ON_REF_PTR,
+    methods::ERR_EXPECT,
     methods::EXPECT_FUN_CALL,
     methods::EXPECT_USED,
     methods::EXTEND_WITH_DRAIN,
     methods::MAP_FLATTEN,
     methods::MAP_IDENTITY,
     methods::MAP_UNWRAP_OR,
+    methods::NEEDLESS_OPTION_AS_DEREF,
     methods::NEEDLESS_SPLITN,
     methods::NEW_RET_NO_SELF,
     methods::OK_EXPECT,
     needless_continue::NEEDLESS_CONTINUE,
     needless_for_each::NEEDLESS_FOR_EACH,
     needless_late_init::NEEDLESS_LATE_INIT,
-    needless_option_as_deref::NEEDLESS_OPTION_AS_DEREF,
     needless_pass_by_value::NEEDLESS_PASS_BY_VALUE,
     needless_question_mark::NEEDLESS_QUESTION_MARK,
     needless_update::NEEDLESS_UPDATE,
     types::TYPE_COMPLEXITY,
     types::VEC_BOX,
     undocumented_unsafe_blocks::UNDOCUMENTED_UNSAFE_BLOCKS,
-    undropped_manually_drops::UNDROPPED_MANUALLY_DROPS,
     unicode::INVISIBLE_CHARACTERS,
     unicode::NON_ASCII_LITERAL,
     unicode::UNICODE_NOT_NFC,
index 6ab139b2fb67b551a80d88a6cf17638111539f01..4802dd877e99d12d90d85d124d164d034bad14ef 100644 (file)
@@ -16,6 +16,7 @@
     LintId::of(default_union_representation::DEFAULT_UNION_REPRESENTATION),
     LintId::of(disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS),
     LintId::of(else_if_without_else::ELSE_IF_WITHOUT_ELSE),
+    LintId::of(empty_structs_with_brackets::EMPTY_STRUCTS_WITH_BRACKETS),
     LintId::of(exhaustive_items::EXHAUSTIVE_ENUMS),
     LintId::of(exhaustive_items::EXHAUSTIVE_STRUCTS),
     LintId::of(exit::EXIT),
index dcf399cf9562f3d7c814b8b04b75b2299d336fab..3114afac8863e69f574e172383fcc8f50414f50b 100644 (file)
@@ -59,6 +59,7 @@
     LintId::of(methods::BYTES_NTH),
     LintId::of(methods::CHARS_LAST_CMP),
     LintId::of(methods::CHARS_NEXT_CMP),
+    LintId::of(methods::ERR_EXPECT),
     LintId::of(methods::INTO_ITER_ON_REF),
     LintId::of(methods::ITER_CLONED_COLLECT),
     LintId::of(methods::ITER_NEXT_SLICE),
index fa3a88e1368ce5cde3ff9aee911e993d4231c604..82f45b5fd58b9691e7c13360c9fe5821de283f4d 100644 (file)
@@ -7,8 +7,12 @@
     LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
+    LintId::of(casts::CAST_ABS_TO_UNSIGNED),
     LintId::of(casts::CAST_ENUM_CONSTRUCTOR),
     LintId::of(casts::CAST_ENUM_TRUNCATION),
+    LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
+    LintId::of(drop_forget_ref::DROP_NON_DROP),
+    LintId::of(drop_forget_ref::FORGET_NON_DROP),
     LintId::of(eval_order_dependence::EVAL_ORDER_DEPENDENCE),
     LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
     LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
index f2a07999144482e48231ba3e4bf19edbf4d71d77..c9b836f95808a623d02b567e5b5ca2f502aa8e33 100644 (file)
@@ -1,5 +1,6 @@
 // error-pattern:cargo-clippy
 
+#![feature(array_windows)]
 #![feature(binary_heap_into_iter_sorted)]
 #![feature(box_patterns)]
 #![feature(control_flow_enum)]
@@ -190,6 +191,7 @@ macro_rules! declare_clippy_lint {
 mod comparison_chain;
 mod copies;
 mod copy_iterator;
+mod crate_in_macro_def;
 mod create_dir;
 mod dbg_macro;
 mod default;
@@ -208,6 +210,7 @@ macro_rules! declare_clippy_lint {
 mod duration_subsec;
 mod else_if_without_else;
 mod empty_enum;
+mod empty_structs_with_brackets;
 mod entry;
 mod enum_clike;
 mod enum_variants;
@@ -305,7 +308,6 @@ macro_rules! declare_clippy_lint {
 mod needless_continue;
 mod needless_for_each;
 mod needless_late_init;
-mod needless_option_as_deref;
 mod needless_pass_by_value;
 mod needless_question_mark;
 mod needless_update;
@@ -375,7 +377,6 @@ macro_rules! declare_clippy_lint {
 mod try_err;
 mod types;
 mod undocumented_unsafe_blocks;
-mod undropped_manually_drops;
 mod unicode;
 mod uninit_vec;
 mod unit_hash;
@@ -533,7 +534,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(ptr::Ptr));
     store.register_late_pass(|| Box::new(ptr_eq::PtrEq));
     store.register_late_pass(|| Box::new(needless_bool::NeedlessBool));
-    store.register_late_pass(|| Box::new(needless_option_as_deref::OptionNeedlessDeref));
     store.register_late_pass(|| Box::new(needless_bool::BoolComparison));
     store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach));
     store.register_late_pass(|| Box::new(misc::MiscLints));
@@ -812,7 +812,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || Box::new(disallowed_methods::DisallowedMethods::new(disallowed_methods.clone())));
     store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
     store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
-    store.register_late_pass(|| Box::new(undropped_manually_drops::UndroppedManuallyDrops));
     store.register_late_pass(|| Box::new(strings::StrToString));
     store.register_late_pass(|| Box::new(strings::StringToString));
     store.register_late_pass(|| Box::new(zero_sized_map_values::ZeroSizedMapValues));
@@ -847,7 +846,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
             enable_raw_pointer_heuristic_for_send,
         ))
     });
-    store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks::default()));
+    store.register_late_pass(move || Box::new(undocumented_unsafe_blocks::UndocumentedUnsafeBlocks));
     store.register_late_pass(|| Box::new(match_str_case_mismatch::MatchStrCaseMismatch));
     store.register_late_pass(move || Box::new(format_args::FormatArgs));
     store.register_late_pass(|| Box::new(trailing_empty_array::TrailingEmptyArray));
@@ -867,6 +866,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
             ignore_publish: cargo_ignore_publish,
         })
     });
+    store.register_early_pass(|| Box::new(crate_in_macro_def::CrateInMacroDef));
+    store.register_early_pass(|| Box::new(empty_structs_with_brackets::EmptyStructsWithBrackets));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
index 36ecd83f7d643734e0f0f2df0977cd63f3c5ea70..a0bd7ad0ac647fb7d0f606e932b15a7e5a5031b3 100644 (file)
@@ -2,9 +2,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{indent_of, snippet_with_applicability};
 use if_chain::if_chain;
+use rustc_ast::util::parser::PREC_PREFIX;
+use rustc_ast::Mutability;
 use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind, Pat};
+use rustc_hir::{is_range_literal, BorrowKind, Expr, ExprKind, Pat};
 use rustc_lint::LateContext;
+use rustc_span::edition::Edition;
 
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
@@ -13,31 +16,84 @@ pub(super) fn check<'tcx>(
     body: &'tcx Expr<'_>,
     expr: &'tcx Expr<'_>,
 ) {
-    let arg_expr = match arg.kind {
-        ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg,
-        ExprKind::MethodCall(method, [arg], _) if method.ident.name == rustc_span::sym::iter => arg,
+    let (arg_expression, prefix) = match arg.kind {
+        ExprKind::AddrOf(
+            BorrowKind::Ref,
+            Mutability::Not,
+            Expr {
+                kind: ExprKind::Array([arg]),
+                ..
+            },
+        ) => (arg, "&"),
+        ExprKind::AddrOf(
+            BorrowKind::Ref,
+            Mutability::Mut,
+            Expr {
+                kind: ExprKind::Array([arg]),
+                ..
+            },
+        ) => (arg, "&mut "),
+        ExprKind::MethodCall(
+            method,
+            [
+                Expr {
+                    kind: ExprKind::Array([arg]),
+                    ..
+                },
+            ],
+            _,
+        ) if method.ident.name == rustc_span::sym::iter => (arg, "&"),
+        ExprKind::MethodCall(
+            method,
+            [
+                Expr {
+                    kind: ExprKind::Array([arg]),
+                    ..
+                },
+            ],
+            _,
+        ) if method.ident.name.as_str() == "iter_mut" => (arg, "&mut "),
+        ExprKind::MethodCall(
+            method,
+            [
+                Expr {
+                    kind: ExprKind::Array([arg]),
+                    ..
+                },
+            ],
+            _,
+        ) if method.ident.name == rustc_span::sym::into_iter => (arg, ""),
+        // Only check for arrays edition 2021 or later, as this case will trigger a compiler error otherwise.
+        ExprKind::Array([arg]) if cx.tcx.sess.edition() >= Edition::Edition2021 => (arg, ""),
         _ => return,
     };
     if_chain! {
-        if let ExprKind::Array([arg_expression]) = arg_expr.kind;
         if let ExprKind::Block(block, _) = body.kind;
         if !block.stmts.is_empty();
         then {
             let mut applicability = Applicability::MachineApplicable;
             let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability);
-            let arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);
+            let mut arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);
             let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned();
             block_str.remove(0);
             block_str.pop();
             let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0));
 
+            // Reference iterator from `&(mut) []` or `[].iter(_mut)()`.
+            if !prefix.is_empty() && (
+                // Precedence of internal expression is less than or equal to precedence of `&expr`.
+                arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression)
+            ) {
+                arg_snip = format!("({arg_snip})").into();
+            }
+
             span_lint_and_sugg(
                 cx,
                 SINGLE_ELEMENT_LOOP,
                 expr.span,
                 "for loop over a single element",
                 "try",
-                format!("{{\n{}let {} = &{};{}}}", indent, pat_snip, arg_snip, block_str),
+                format!("{{\n{indent}let {pat_snip} = {prefix}{arg_snip};{block_str}}}"),
                 applicability,
             )
         }
index 0f6ac47843241d9cc3718d8548eea50ecb746b9b..f552d5c1afab9268f4448af24acbd7e881cd2a84 100644 (file)
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet;
+use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_context};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{iter_input_pats, method_chain_args};
 use if_chain::if_chain;
@@ -217,36 +217,33 @@ fn lint_map_unit_fn(cx: &LateContext<'_>, stmt: &hir::Stmt<'_>, expr: &hir::Expr
     let fn_arg = &map_args[1];
 
     if is_unit_function(cx, fn_arg) {
+        let mut applicability = Applicability::MachineApplicable;
         let msg = suggestion_msg("function", map_type);
         let suggestion = format!(
             "if let {0}({binding}) = {1} {{ {2}({binding}) }}",
             variant,
-            snippet(cx, var_arg.span, "_"),
-            snippet(cx, fn_arg.span, "_"),
+            snippet_with_applicability(cx, var_arg.span, "_", &mut applicability),
+            snippet_with_applicability(cx, fn_arg.span, "_", &mut applicability),
             binding = let_binding_name(cx, var_arg)
         );
 
         span_lint_and_then(cx, lint, expr.span, &msg, |diag| {
-            diag.span_suggestion(stmt.span, "try this", suggestion, Applicability::MachineApplicable);
+            diag.span_suggestion(stmt.span, "try this", suggestion, applicability);
         });
     } else if let Some((binding, closure_expr)) = unit_closure(cx, fn_arg) {
         let msg = suggestion_msg("closure", map_type);
 
         span_lint_and_then(cx, lint, expr.span, &msg, |diag| {
             if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) {
+                let mut applicability = Applicability::MachineApplicable;
                 let suggestion = format!(
                     "if let {0}({1}) = {2} {{ {3} }}",
                     variant,
-                    snippet(cx, binding.pat.span, "_"),
-                    snippet(cx, var_arg.span, "_"),
-                    snippet(cx, reduced_expr_span, "_")
-                );
-                diag.span_suggestion(
-                    stmt.span,
-                    "try this",
-                    suggestion,
-                    Applicability::MachineApplicable, // snippet
+                    snippet_with_applicability(cx, binding.pat.span, "_", &mut applicability),
+                    snippet_with_applicability(cx, var_arg.span, "_", &mut applicability),
+                    snippet_with_context(cx, reduced_expr_span, var_arg.span.ctxt(), "_", &mut applicability).0,
                 );
+                diag.span_suggestion(stmt.span, "try this", suggestion, applicability);
             } else {
                 let suggestion = format!(
                     "if let {0}({1}) = {2} {{ ... }}",
index ff85623acf49b85eabdd4c4eb4e399e9537ceefa..e93b494653fc05923feecace84790bb7606d6122 100644 (file)
@@ -667,7 +667,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                     overlapping_arms::check(cx, ex, arms);
                     match_wild_enum::check(cx, ex, arms);
                     match_as_ref::check(cx, ex, arms, expr);
-                    needless_match::check_match(cx, ex, arms);
+                    needless_match::check_match(cx, ex, arms, expr);
 
                     if self.infallible_destructuring_match_linted {
                         self.infallible_destructuring_match_linted = false;
index 76131d307d777e10e262cba3971b4b890a6c8c49..2105a03e03a301cb8cca7aba53623766dd1fdc6e 100644 (file)
@@ -1,37 +1,25 @@
 use super::NEEDLESS_MATCH;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{eq_expr_value, get_parent_expr, higher, is_else_clause, is_lang_ctor, peel_blocks_with_stmt};
+use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts};
+use clippy_utils::{
+    eq_expr_value, get_parent_expr_for_hir, get_parent_node, higher, is_else_clause, is_lang_ctor, over,
+    peel_blocks_with_stmt,
+};
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::OptionNone;
-use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, Pat, PatKind, Path, PathSegment, QPath, UnOp};
+use rustc_hir::{Arm, BindingAnnotation, Expr, ExprKind, FnRetTy, Node, Pat, PatKind, Path, QPath};
 use rustc_lint::LateContext;
 use rustc_span::sym;
+use rustc_typeck::hir_ty_to_ty;
 
-pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
-    // This is for avoiding collision with `match_single_binding`.
-    if arms.len() < 2 {
-        return;
-    }
-
-    for arm in arms {
-        if let PatKind::Wild = arm.pat.kind {
-            let ret_expr = strip_return(arm.body);
-            if !eq_expr_value(cx, ex, ret_expr) {
-                return;
-            }
-        } else if !pat_same_as_expr(arm.pat, arm.body) {
-            return;
-        }
-    }
-
-    if let Some(match_expr) = get_parent_expr(cx, ex) {
+pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
+    if arms.len() > 1 && expr_ty_matches_p_ty(cx, ex, expr) && check_all_arms(cx, ex, arms) {
         let mut applicability = Applicability::MachineApplicable;
         span_lint_and_sugg(
             cx,
             NEEDLESS_MATCH,
-            match_expr.span,
+            expr.span,
             "this match expression is unnecessary",
             "replace it with",
             snippet_with_applicability(cx, ex.span, "..", &mut applicability).to_string(),
@@ -60,11 +48,8 @@ pub(crate) fn check_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>])
 /// }
 /// ```
 pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) {
-    if_chain! {
-        if let Some(ref if_let) = higher::IfLet::hir(cx, ex);
-        if !is_else_clause(cx.tcx, ex);
-        if check_if_let(cx, if_let);
-        then {
+    if let Some(ref if_let) = higher::IfLet::hir(cx, ex) {
+        if !is_else_clause(cx.tcx, ex) && expr_ty_matches_p_ty(cx, if_let.let_expr, ex) && check_if_let(cx, if_let) {
             let mut applicability = Applicability::MachineApplicable;
             span_lint_and_sugg(
                 cx,
@@ -79,6 +64,19 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>) {
     }
 }
 
+fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>]) -> bool {
+    for arm in arms {
+        let arm_expr = peel_blocks_with_stmt(arm.body);
+        if let PatKind::Wild = arm.pat.kind {
+            return eq_expr_value(cx, match_expr, strip_return(arm_expr));
+        } else if !pat_same_as_expr(arm.pat, arm_expr) {
+            return false;
+        }
+    }
+
+    true
+}
+
 fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
     if let Some(if_else) = if_let.if_else {
         if !pat_same_as_expr(if_let.let_pat, peel_blocks_with_stmt(if_let.if_then)) {
@@ -92,18 +90,21 @@ fn check_if_let(cx: &LateContext<'_>, if_let: &higher::IfLet<'_>) -> bool {
 
         if matches!(if_else.kind, ExprKind::Block(..)) {
             let else_expr = peel_blocks_with_stmt(if_else);
+            if matches!(else_expr.kind, ExprKind::Block(..)) {
+                return false;
+            }
             let ret = strip_return(else_expr);
             let let_expr_ty = cx.typeck_results().expr_ty(if_let.let_expr);
             if is_type_diagnostic_item(cx, let_expr_ty, sym::Option) {
                 if let ExprKind::Path(ref qpath) = ret.kind {
                     return is_lang_ctor(cx, qpath, OptionNone) || eq_expr_value(cx, if_let.let_expr, ret);
                 }
-            } else {
-                return eq_expr_value(cx, if_let.let_expr, ret);
+                return true;
             }
-            return true;
+            return eq_expr_value(cx, if_let.let_expr, ret);
         }
     }
+
     false
 }
 
@@ -116,48 +117,70 @@ fn strip_return<'hir>(expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
     }
 }
 
+/// Manually check for coercion casting by checking if the type of the match operand or let expr
+/// differs with the assigned local variable or the funtion return type.
+fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>) -> bool {
+    if let Some(p_node) = get_parent_node(cx.tcx, p_expr.hir_id) {
+        match p_node {
+            // Compare match_expr ty with local in `let local = match match_expr {..}`
+            Node::Local(local) => {
+                let results = cx.typeck_results();
+                return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr));
+            },
+            // compare match_expr ty with RetTy in `fn foo() -> RetTy`
+            Node::Item(..) => {
+                if let Some(fn_decl) = p_node.fn_decl() {
+                    if let FnRetTy::Return(ret_ty) = fn_decl.output {
+                        return same_type_and_consts(hir_ty_to_ty(cx.tcx, ret_ty), cx.typeck_results().expr_ty(expr));
+                    }
+                }
+            },
+            // check the parent expr for this whole block `{ match match_expr {..} }`
+            Node::Block(block) => {
+                if let Some(block_parent_expr) = get_parent_expr_for_hir(cx, block.hir_id) {
+                    return expr_ty_matches_p_ty(cx, expr, block_parent_expr);
+                }
+            },
+            // recursively call on `if xxx {..}` etc.
+            Node::Expr(p_expr) => {
+                return expr_ty_matches_p_ty(cx, expr, p_expr);
+            },
+            _ => {},
+        }
+    }
+    false
+}
+
 fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
     let expr = strip_return(expr);
     match (&pat.kind, &expr.kind) {
         // Example: `Some(val) => Some(val)`
-        (
-            PatKind::TupleStruct(QPath::Resolved(_, path), [first_pat, ..], _),
-            ExprKind::Call(call_expr, [first_param, ..]),
-        ) => {
+        (PatKind::TupleStruct(QPath::Resolved(_, path), tuple_params, _), ExprKind::Call(call_expr, call_params)) => {
             if let ExprKind::Path(QPath::Resolved(_, call_path)) = call_expr.kind {
-                if has_identical_segments(path.segments, call_path.segments)
-                    && has_same_non_ref_symbol(first_pat, first_param)
-                {
-                    return true;
-                }
+                return over(path.segments, call_path.segments, |pat_seg, call_seg| {
+                    pat_seg.ident.name == call_seg.ident.name
+                }) && same_non_ref_symbols(tuple_params, call_params);
             }
         },
-        // Example: `val => val`, or `ref val => *val`
-        (PatKind::Binding(annot, _, pat_ident, _), _) => {
-            let new_expr = if let (
-                BindingAnnotation::Ref | BindingAnnotation::RefMut,
-                ExprKind::Unary(UnOp::Deref, operand_expr),
-            ) = (annot, &expr.kind)
-            {
-                operand_expr
-            } else {
-                expr
-            };
-
-            if let ExprKind::Path(QPath::Resolved(
+        // Example: `val => val`
+        (
+            PatKind::Binding(annot, _, pat_ident, _),
+            ExprKind::Path(QPath::Resolved(
                 _,
                 Path {
                     segments: [first_seg, ..],
                     ..
                 },
-            )) = new_expr.kind
-            {
-                return pat_ident.name == first_seg.ident.name;
-            }
+            )),
+        ) => {
+            return !matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut)
+                && pat_ident.name == first_seg.ident.name;
         },
         // Example: `Custom::TypeA => Custom::TypeB`, or `None => None`
         (PatKind::Path(QPath::Resolved(_, p_path)), ExprKind::Path(QPath::Resolved(_, e_path))) => {
-            return has_identical_segments(p_path.segments, e_path.segments);
+            return over(p_path.segments, e_path.segments, |p_seg, e_seg| {
+                p_seg.ident.name == e_seg.ident.name
+            });
         },
         // Example: `5 => 5`
         (PatKind::Lit(pat_lit_expr), ExprKind::Lit(expr_spanned)) => {
@@ -171,27 +194,16 @@ fn pat_same_as_expr(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
     false
 }
 
-fn has_identical_segments(left_segs: &[PathSegment<'_>], right_segs: &[PathSegment<'_>]) -> bool {
-    if left_segs.len() != right_segs.len() {
+fn same_non_ref_symbols(pats: &[Pat<'_>], exprs: &[Expr<'_>]) -> bool {
+    if pats.len() != exprs.len() {
         return false;
     }
-    for i in 0..left_segs.len() {
-        if left_segs[i].ident.name != right_segs[i].ident.name {
-            return false;
-        }
-    }
-    true
-}
 
-fn has_same_non_ref_symbol(pat: &Pat<'_>, expr: &Expr<'_>) -> bool {
-    if_chain! {
-        if let PatKind::Binding(annot, _, pat_ident, _) = pat.kind;
-        if !matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
-        if let ExprKind::Path(QPath::Resolved(_, Path {segments: [first_seg, ..], .. })) = expr.kind;
-        then {
-            return pat_ident.name == first_seg.ident.name;
+    for i in 0..pats.len() {
+        if !pat_same_as_expr(&pats[i], &exprs[i]) {
+            return false;
         }
     }
 
-    false
+    true
 }
index 76eaedea8a0d229b1d5ff32282db7eb52ddfc1f0..44857d61fef8f9461e03eca830e4adccf882ca88 100644 (file)
@@ -22,7 +22,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, recv: &'tcx E
         cx,
         BYTES_NTH,
         expr.span,
-        &format!("called `.byte().nth()` on a `{}`", caller_type),
+        &format!("called `.bytes().nth()` on a `{}`", caller_type),
         "try",
         format!(
             "{}.as_bytes().get({})",
diff --git a/clippy_lints/src/methods/err_expect.rs b/clippy_lints/src/methods/err_expect.rs
new file mode 100644 (file)
index 0000000..be9d4ad
--- /dev/null
@@ -0,0 +1,60 @@
+use super::ERR_EXPECT;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{meets_msrv, msrvs, ty::is_type_diagnostic_item};
+use rustc_errors::Applicability;
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_middle::ty::Ty;
+use rustc_semver::RustcVersion;
+use rustc_span::{sym, Span};
+
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    _expr: &rustc_hir::Expr<'_>,
+    recv: &rustc_hir::Expr<'_>,
+    msrv: Option<&RustcVersion>,
+    expect_span: Span,
+    err_span: Span,
+) {
+    if_chain! {
+        if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
+        // Test the version to make sure the lint can be showed (expect_err has been
+        // introduced in rust 1.17.0 : https://github.com/rust-lang/rust/pull/38982)
+        if meets_msrv(msrv, &msrvs::EXPECT_ERR);
+
+        // Grabs the `Result<T, E>` type
+        let result_type = cx.typeck_results().expr_ty(recv);
+        // Tests if the T type in a `Result<T, E>` is not None
+        if let Some(data_type) = get_data_type(cx, result_type);
+        // Tests if the T type in a `Result<T, E>` implements debug
+        if has_debug_impl(data_type, cx);
+
+        then {
+            span_lint_and_sugg(
+                cx,
+                ERR_EXPECT,
+                err_span.to(expect_span),
+                "called `.err().expect()` on a `Result` value",
+                "try",
+                "expect_err".to_string(),
+                Applicability::MachineApplicable
+        );
+        }
+    };
+}
+
+/// Given a `Result<T, E>` type, return its data (`T`).
+fn get_data_type<'a>(cx: &LateContext<'_>, ty: Ty<'a>) -> Option<Ty<'a>> {
+    match ty.kind() {
+        ty::Adt(_, substs) if is_type_diagnostic_item(cx, ty, sym::Result) => substs.types().next(),
+        _ => None,
+    }
+}
+
+/// Given a type, very if the Debug trait has been impl'd
+fn has_debug_impl<'tcx>(ty: Ty<'tcx>, cx: &LateContext<'tcx>) -> bool {
+    cx.tcx
+        .get_diagnostic_item(sym::Debug)
+        .map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
+}
index c98cdfbca434e9dd9bfbdcede516e322be519e97..9651a52be4e7281a28a4891a942c5d415d3de761 100644 (file)
@@ -48,13 +48,11 @@ pub fn is_clone_like(cx: &LateContext<'_>, method_name: &str, method_def_id: hir
         "to_os_string" => is_diag_item_method(cx, method_def_id, sym::OsStr),
         "to_owned" => is_diag_trait_item(cx, method_def_id, sym::ToOwned),
         "to_path_buf" => is_diag_item_method(cx, method_def_id, sym::Path),
-        "to_vec" => {
-            cx.tcx.impl_of_method(method_def_id)
-                .filter(|&impl_did| {
-                    cx.tcx.type_of(impl_did).is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none()
-                })
-                .is_some()
-        },
+        "to_vec" => cx
+            .tcx
+            .impl_of_method(method_def_id)
+            .filter(|&impl_did| cx.tcx.type_of(impl_did).is_slice() && cx.tcx.impl_trait_ref(impl_did).is_none())
+            .is_some(),
         _ => false,
     }
 }
index b93f1399eaeed8e9c13692355049678cf450cb42..54c9ca435a447d8e870403ecf78cc48a5ce7d09a 100644 (file)
@@ -1,11 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
-use clippy_utils::ty::{get_iterator_item_ty, is_copy};
+use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy};
 use itertools::Itertools;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
+use rustc_span::sym;
 use std::ops::Not;
 
 use super::ITER_OVEREAGER_CLONED;
@@ -20,9 +21,16 @@ pub(super) fn check<'tcx>(
     map_arg: &[hir::Expr<'_>],
 ) {
     // Check if it's iterator and get type associated with `Item`.
-    let inner_ty = match get_iterator_item_ty(cx, cx.typeck_results().expr_ty_adjusted(recv)) {
-        Some(ty) => ty,
-        _ => return,
+    let inner_ty = if_chain! {
+        if let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
+        let recv_ty = cx.typeck_results().expr_ty(recv);
+        if implements_trait(cx, recv_ty, iterator_trait_id, &[]);
+        if let Some(inner_ty) = get_iterator_item_ty(cx, cx.typeck_results().expr_ty_adjusted(recv));
+        then {
+            inner_ty
+        } else {
+            return;
+        }
     };
 
     match inner_ty.kind() {
index f112b500d3d296051a3c43033383e45bd92a1458..862a9578e6ff26d76296e18ad7b78a9e53ec36d0 100644 (file)
@@ -13,6 +13,7 @@ pub(super) fn check(
     expr: &hir::Expr<'_>,
     caller: &hir::Expr<'_>,
     map_arg: &hir::Expr<'_>,
+    name: &str,
     _map_span: Span,
 ) {
     let caller_ty = cx.typeck_results().expr_ty(caller);
@@ -29,7 +30,7 @@ pub(super) fn check(
                 MAP_IDENTITY,
                 sugg_span,
                 "unnecessary map of the identity function",
-                "remove the call to `map`",
+                &format!("remove the call to `{}`", name),
                 String::new(),
                 Applicability::MachineApplicable,
             )
index 9d4e1fa39940139b1c1248df21649db552b66cd7..70d021a1668eb7ffb68b2cce0a79cc5c50d6195d 100644 (file)
@@ -9,6 +9,7 @@
 mod clone_on_copy;
 mod clone_on_ref_ptr;
 mod cloned_instead_of_copied;
+mod err_expect;
 mod expect_fun_call;
 mod expect_used;
 mod extend_with_drain;
@@ -40,6 +41,7 @@
 mod map_flatten;
 mod map_identity;
 mod map_unwrap_or;
+mod needless_option_as_deref;
 mod ok_expect;
 mod option_as_ref_deref;
 mod option_map_or_none;
     "using `ok().expect()`, which gives worse error messages than calling `expect` directly on the Result"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `.err().expect()` calls on the `Result` type.
+    ///
+    /// ### Why is this bad?
+    /// `.expect_err()` can be called directly to avoid the extra type conversion from `err()`.
+    ///
+    /// ### Example
+    /// ```should_panic
+    /// let x: Result<u32, &str> = Ok(10);
+    /// x.err().expect("Testing err().expect()");
+    /// ```
+    /// Use instead:
+    /// ```should_panic
+    /// let x: Result<u32, &str> = Ok(10);
+    /// x.expect_err("Testing expect_err");
+    /// ```
+    #[clippy::version = "1.61.0"]
+    pub ERR_EXPECT,
+    style,
+    r#"using `.err().expect("")` when `.expect_err("")` can be used"#
+}
+
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for usages of `_.unwrap_or_else(Default::default)` on `Option` and
     /// Checks for use of `.collect::<Vec<String>>().join("")` on iterators.
     ///
     /// ### Why is this bad?
-    /// `.collect::<String>()` is more concise and usually more performant
+    /// `.collect::<String>()` is more concise and might be more performant
     ///
     /// ### Example
     /// ```rust
     /// println!("{}", output);
     /// ```
     /// ### Known problems
-    /// While `.collect::<String>()` is more performant in most cases, there are cases where
+    /// While `.collect::<String>()` is sometimes more performant, there are cases where
     /// using `.collect::<String>()` over `.collect::<Vec<String>>().join("")`
     /// will prevent loop unrolling and will result in a negative performance impact.
+    ///
+    /// Additionlly, differences have been observed between aarch64 and x86_64 assembly output,
+    /// with aarch64 tending to producing faster assembly in more cases when using `.collect::<String>()`
     #[clippy::version = "1.61.0"]
     pub UNNECESSARY_JOIN,
     pedantic,
     "using `.collect::<Vec<String>>().join(\"\")` on an iterator"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for no-op uses of `Option::{as_deref, as_deref_mut}`,
+    /// for example, `Option<&T>::as_deref()` returns the same type.
+    ///
+    /// ### Why is this bad?
+    /// Redundant code and improving readability.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let a = Some(&1);
+    /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
+    /// ```
+    /// Could be written as:
+    /// ```rust
+    /// let a = Some(&1);
+    /// let b = a;
+    /// ```
+    #[clippy::version = "1.57.0"]
+    pub NEEDLESS_OPTION_AS_DEREF,
+    complexity,
+    "no-op use of `deref` or `deref_mut` method to `Option`."
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Option<RustcVersion>,
@@ -2165,6 +2217,8 @@ pub fn new(avoid_breaking_exported_api: bool, msrv: Option<RustcVersion>) -> Sel
     NEEDLESS_SPLITN,
     UNNECESSARY_TO_OWNED,
     UNNECESSARY_JOIN,
+    ERR_EXPECT,
+    NEEDLESS_OPTION_AS_DEREF,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -2397,6 +2451,9 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                     unnecessary_lazy_eval::check(cx, expr, recv, arg, "and");
                 }
             },
+            ("as_deref" | "as_deref_mut", []) => {
+                needless_option_as_deref::check(cx, expr, recv, name);
+            },
             ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
             ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
             ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
@@ -2428,6 +2485,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
             },
             ("expect", [_]) => match method_call(recv) {
                 Some(("ok", [recv], _)) => ok_expect::check(cx, expr, recv),
+                Some(("err", [recv], err_span)) => err_expect::check(cx, expr, recv, msrv, span, err_span),
                 _ => expect_used::check(cx, expr, recv),
             },
             ("extend", [arg]) => {
@@ -2472,7 +2530,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                     }
                 }
             },
-            ("map", [m_arg]) => {
+            (name @ ("map" | "map_err"), [m_arg]) => {
                 if let Some((name, [recv2, args @ ..], span2)) = method_call(recv) {
                     match (name, args) {
                         ("as_mut", []) => option_as_ref_deref::check(cx, expr, recv2, m_arg, true, msrv),
@@ -2484,7 +2542,7 @@ fn check_methods<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, msrv: Optio
                         _ => {},
                     }
                 }
-                map_identity::check(cx, expr, recv, m_arg, span);
+                map_identity::check(cx, expr, recv, m_arg, name, span);
             },
             ("map_or", [def, map]) => option_map_or_none::check(cx, expr, recv, def, map),
             (name @ "next", args @ []) => {
diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs
new file mode 100644 (file)
index 0000000..7030baf
--- /dev/null
@@ -0,0 +1,37 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::path_res;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::usage::local_used_after_expr;
+use rustc_errors::Applicability;
+use rustc_hir::def::Res;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::NEEDLESS_OPTION_AS_DEREF;
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name: &str) {
+    let typeck = cx.typeck_results();
+    let outer_ty = typeck.expr_ty(expr);
+
+    if is_type_diagnostic_item(cx, outer_ty, sym::Option) && outer_ty == typeck.expr_ty(recv) {
+        if name == "as_deref_mut" && recv.is_syntactic_place_expr() {
+            let Res::Local(binding_id) = path_res(cx, recv) else { return };
+
+            if local_used_after_expr(cx, binding_id, recv) {
+                return;
+            }
+        }
+
+        span_lint_and_sugg(
+            cx,
+            NEEDLESS_OPTION_AS_DEREF,
+            expr.span,
+            "derefed type is same as origin",
+            "try this",
+            snippet_opt(cx, recv.span).unwrap(),
+            Applicability::MachineApplicable,
+        );
+    }
+}
index b8dfe996880661e118ec06aa357a4606a4d1815b..0a393657267b07833371498eb52b5015faf8c874 100644 (file)
@@ -1,17 +1,14 @@
-use std::{
-    ffi::OsString,
-    path::{Component, Path},
-};
-
 use rustc_ast::ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{FileName, RealFileName, SourceFile, Span, SyntaxContext};
+use std::ffi::OsStr;
+use std::path::{Component, Path};
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks that module layout uses only self named module files, bans mod.rs files.
+    /// Checks that module layout uses only self named module files, bans `mod.rs` files.
     ///
     /// ### Why is this bad?
     /// Having multiple module layout styles in a project can be confusing.
@@ -40,7 +37,7 @@
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks that module layout uses only mod.rs files.
+    /// Checks that module layout uses only `mod.rs` files.
     ///
     /// ### Why is this bad?
     /// Having multiple module layout styles in a project can be confusing.
@@ -82,11 +79,7 @@ fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
 
         let files = cx.sess().source_map().files();
 
-        let trim_to_src = if let RealFileName::LocalPath(p) = &cx.sess().opts.working_dir {
-            p.to_string_lossy()
-        } else {
-            return;
-        };
+        let RealFileName::LocalPath(trim_to_src) = &cx.sess().opts.working_dir else { return };
 
         // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives
         // `[path, to]` but not foo
@@ -97,26 +90,27 @@ fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
         // `{ foo => path/to/foo.rs, .. }
         let mut file_map = FxHashMap::default();
         for file in files.iter() {
-            match &file.name {
-                FileName::Real(RealFileName::LocalPath(lp))
-                    if lp.to_string_lossy().starts_with(trim_to_src.as_ref()) =>
-                {
-                    let p = lp.to_string_lossy();
-                    let path = Path::new(p.trim_start_matches(trim_to_src.as_ref()));
-                    if let Some(stem) = path.file_stem() {
-                        file_map.insert(stem.to_os_string(), (file, path.to_owned()));
-                    }
-                    process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders);
-                    check_self_named_mod_exists(cx, path, file);
-                },
-                _ => {},
+            if let FileName::Real(RealFileName::LocalPath(lp)) = &file.name {
+                let path = if lp.is_relative() {
+                    lp
+                } else if let Ok(relative) = lp.strip_prefix(trim_to_src) {
+                    relative
+                } else {
+                    continue;
+                };
+
+                if let Some(stem) = path.file_stem() {
+                    file_map.insert(stem, (file, path));
+                }
+                process_paths_for_mod_files(path, &mut folder_segments, &mut mod_folders);
+                check_self_named_mod_exists(cx, path, file);
             }
         }
 
         for folder in &folder_segments {
             if !mod_folders.contains(folder) {
                 if let Some((file, path)) = file_map.get(folder) {
-                    let mut correct = path.clone();
+                    let mut correct = path.to_path_buf();
                     correct.pop();
                     correct.push(folder);
                     correct.push("mod.rs");
@@ -138,25 +132,17 @@ fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
 
 /// For each `path` we add each folder component to `folder_segments` and if the file name
 /// is `mod.rs` we add it's parent folder to `mod_folders`.
-fn process_paths_for_mod_files(
-    path: &Path,
-    folder_segments: &mut FxHashSet<OsString>,
-    mod_folders: &mut FxHashSet<OsString>,
+fn process_paths_for_mod_files<'a>(
+    path: &'a Path,
+    folder_segments: &mut FxHashSet<&'a OsStr>,
+    mod_folders: &mut FxHashSet<&'a OsStr>,
 ) {
     let mut comp = path.components().rev().peekable();
     let _ = comp.next();
     if path.ends_with("mod.rs") {
-        mod_folders.insert(comp.peek().map(|c| c.as_os_str().to_owned()).unwrap_or_default());
+        mod_folders.insert(comp.peek().map(|c| c.as_os_str()).unwrap_or_default());
     }
-    let folders = comp
-        .filter_map(|c| {
-            if let Component::Normal(s) = c {
-                Some(s.to_os_string())
-            } else {
-                None
-            }
-        })
-        .collect::<Vec<_>>();
+    let folders = comp.filter_map(|c| if let Component::Normal(s) = c { Some(s) } else { None });
     folder_segments.extend(folders);
 }
 
diff --git a/clippy_lints/src/needless_option_as_deref.rs b/clippy_lints/src/needless_option_as_deref.rs
deleted file mode 100644 (file)
index 9d3d7d1..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
-use clippy_utils::ty::is_type_diagnostic_item;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::symbol::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for no-op uses of Option::{as_deref,as_deref_mut},
-    /// for example, `Option<&T>::as_deref()` returns the same type.
-    ///
-    /// ### Why is this bad?
-    /// Redundant code and improving readability.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let a = Some(&1);
-    /// let b = a.as_deref(); // goes from Option<&i32> to Option<&i32>
-    /// ```
-    /// Could be written as:
-    /// ```rust
-    /// let a = Some(&1);
-    /// let b = a;
-    /// ```
-    #[clippy::version = "1.57.0"]
-    pub NEEDLESS_OPTION_AS_DEREF,
-    complexity,
-    "no-op use of `deref` or `deref_mut` method to `Option`."
-}
-
-declare_lint_pass!(OptionNeedlessDeref=> [
-    NEEDLESS_OPTION_AS_DEREF,
-]);
-
-impl<'tcx> LateLintPass<'tcx> for OptionNeedlessDeref {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if expr.span.from_expansion() {
-            return;
-        }
-        let typeck = cx.typeck_results();
-        let outer_ty = typeck.expr_ty(expr);
-
-        if_chain! {
-            if is_type_diagnostic_item(cx,outer_ty,sym::Option);
-            if let ExprKind::MethodCall(path, [sub_expr], _) = expr.kind;
-            let symbol = path.ident.as_str();
-            if symbol == "as_deref" || symbol == "as_deref_mut";
-            if outer_ty == typeck.expr_ty(sub_expr);
-            then{
-                span_lint_and_sugg(
-                    cx,
-                    NEEDLESS_OPTION_AS_DEREF,
-                    expr.span,
-                    "derefed type is same as origin",
-                    "try this",
-                    snippet_opt(cx,sub_expr.span).unwrap(),
-                    Applicability::MachineApplicable
-                );
-            }
-        }
-    }
-}
index 6ef6b9a20aa4b185211eaa6c96c48dae49d11af0..2f3007658ea6296f8e2b7b1c541f0224cc10fca0 100644 (file)
@@ -78,6 +78,10 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         let Some(macro_call) = root_macro_call_first_node(cx, expr) else { return };
         if is_panic(cx, macro_call.def_id) {
+            if cx.tcx.hir().is_inside_const_context(expr.hir_id) {
+                return;
+            }
+
             span_lint(
                 cx,
                 PANIC,
index 5f453dc16555874f783fd6de9d8d54e462b8edb3..48a2666a2e0cef399b2cfeb4a5fd3e808d9b1655 100644 (file)
@@ -601,9 +601,7 @@ fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
                             },
                             // If the types match check for methods which exist on both types. e.g. `Vec::len` and
                             // `slice::len`
-                            ty::Adt(def, _)
-                                if def.did() == args.ty_did =>
-                            {
+                            ty::Adt(def, _) if def.did() == args.ty_did => {
                                 set_skip_flag();
                             },
                             _ => (),
index 02569bd3a476e50e3b0ea759ad9930712847f481..342f23f030cd06fa3bf3c55355f50f92c2ede39e 100644 (file)
@@ -410,9 +410,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
             if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
             if cx.tcx.is_diagnostic_item(sym::transmute, def_id);
             then {
-                // Avoid suggesting from/to bits and dereferencing raw pointers in const contexts.
-                // See https://github.com/rust-lang/rust/issues/73736 for progress on making them `const fn`.
-                // And see https://github.com/rust-lang/rust/issues/51911 for dereferencing raw pointers.
+                // Avoid suggesting non-const operations in const contexts:
+                // - from/to bits (https://github.com/rust-lang/rust/issues/73736)
+                // - dereferencing raw pointers (https://github.com/rust-lang/rust/issues/51911)
+                // - char conversions (https://github.com/rust-lang/rust/issues/89259)
                 let const_context = in_constant(cx, e.hir_id);
 
                 let from_ty = cx.typeck_results().expr_ty_adjusted(arg);
@@ -427,7 +428,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                 let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
                     | crosspointer_transmute::check(cx, e, from_ty, to_ty)
                     | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, qpath)
-                    | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg)
+                    | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
                     | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
                     | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
                     | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
index 3eb07b68992a89b96ff10c4a40277023c8878536..9e1823c373bfdb4d641536bfd04b10157eda1b1c 100644 (file)
@@ -15,9 +15,10 @@ pub(super) fn check<'tcx>(
     from_ty: Ty<'tcx>,
     to_ty: Ty<'tcx>,
     arg: &'tcx Expr<'_>,
+    const_context: bool,
 ) -> bool {
     match (&from_ty.kind(), &to_ty.kind()) {
-        (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) => {
+        (ty::Int(ty::IntTy::I32) | ty::Uint(ty::UintTy::U32), &ty::Char) if !const_context => {
             span_lint_and_then(
                 cx,
                 TRANSMUTE_INT_TO_CHAR,
index 7570bc2a7a8f0d8195e2f8d560f29f36a06c97b3..786e7bfc56f6ecbb00e442e72bd530f865d26100 100644 (file)
@@ -32,18 +32,20 @@ pub(super) fn check<'tcx>(
                     ""
                 };
 
+                let snippet = snippet(cx, arg.span, "..");
+
                 span_lint_and_sugg(
                     cx,
                     TRANSMUTE_BYTES_TO_STR,
                     e.span,
                     &format!("transmute from a `{}` to a `{}`", from_ty, to_ty),
                     "consider using",
-                    format!(
-                        "std::str::from_utf8{}({}).unwrap()",
-                        postfix,
-                        snippet(cx, arg.span, ".."),
-                    ),
-                    Applicability::Unspecified,
+                    if const_context {
+                        format!("std::str::from_utf8_unchecked{postfix}({snippet})")
+                    } else {
+                        format!("std::str::from_utf8{postfix}({snippet}).unwrap()")
+                    },
+                    Applicability::MaybeIncorrect,
                 );
                 triggered = true;
             } else {
index e42c6c63ede0ba2e419de335318e2bcf841d4a89..c8912a18f1854e3dcf5ee2252b1dcb52ced5d754 100644 (file)
@@ -1,16 +1,13 @@
-use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::is_lint_allowed;
-use clippy_utils::source::{indent_of, reindent_multiline, snippet};
-use rustc_errors::Applicability;
-use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::{Block, BlockCheckMode, Expr, ExprKind, HirId, Local, UnsafeSource};
-use rustc_lexer::TokenKind;
-use rustc_lint::{LateContext, LateLintPass};
+use clippy_utils::source::walk_span_to_context;
+use rustc_data_structures::sync::Lrc;
+use rustc_hir::{Block, BlockCheckMode, UnsafeSource};
+use rustc_lexer::{tokenize, TokenKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::TyCtxt;
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{BytePos, Span};
-use std::borrow::Cow;
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::{BytePos, Pos, SyntaxContext};
 
 declare_clippy_lint! {
     /// ### What it does
     /// explaining why the unsafe operations performed inside
     /// the block are safe.
     ///
+    /// Note the comment must appear on the line(s) preceding the unsafe block
+    /// with nothing appearing in between. The following is ok:
+    /// ```ignore
+    /// foo(
+    ///     // SAFETY:
+    ///     // This is a valid safety comment
+    ///     unsafe { *x }
+    /// )
+    /// ```
+    /// But neither of these are:
+    /// ```ignore
+    /// // SAFETY:
+    /// // This is not a valid safety comment
+    /// foo(
+    ///     /* SAFETY: Neither is this */ unsafe { *x },
+    /// );
+    /// ```
+    ///
     /// ### Why is this bad?
     /// Undocumented unsafe blocks can make it difficult to
     /// read and maintain code, as well as uncover unsoundness
     "creating an unsafe block without explaining why it is safe"
 }
 
-impl_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
-
-#[derive(Default)]
-pub struct UndocumentedUnsafeBlocks {
-    pub local_level: u32,
-    pub local_span: Option<Span>,
-    // The local was already checked for an overall safety comment
-    // There is no need to continue checking the blocks in the local
-    pub local_checked: bool,
-    // Since we can only check the blocks from expanded macros
-    // We have to omit the suggestion due to the actual definition
-    // Not being available to us
-    pub macro_expansion: bool,
-}
+declare_lint_pass!(UndocumentedUnsafeBlocks => [UNDOCUMENTED_UNSAFE_BLOCKS]);
 
 impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
     fn check_block(&mut self, cx: &LateContext<'_>, block: &'_ Block<'_>) {
-        if_chain! {
-            if !self.local_checked;
-            if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id);
-            if !in_external_macro(cx.tcx.sess, block.span);
-            if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules;
-            if let Some(enclosing_scope_hir_id) = cx.tcx.hir().get_enclosing_scope(block.hir_id);
-            if self.block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, block.span) == Some(false);
-            then {
-                let mut span = block.span;
-
-                if let Some(local_span) = self.local_span {
-                    span = local_span;
-
-                    let result = self.block_has_safety_comment(cx.tcx, enclosing_scope_hir_id, span);
+        if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
+            && !in_external_macro(cx.tcx.sess, block.span)
+            && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, block.hir_id)
+            && !is_unsafe_from_proc_macro(cx, block)
+            && !block_has_safety_comment(cx, block)
+        {
+            let source_map = cx.tcx.sess.source_map();
+            let span = if source_map.is_multiline(block.span) {
+                source_map.span_until_char(block.span, '\n')
+            } else {
+                block.span
+            };
 
-                    if result.unwrap_or(true) {
-                        self.local_checked = true;
-                        return;
-                    }
-                }
-
-                self.lint(cx, span);
-            }
-        }
-    }
-
-    fn check_local(&mut self, cx: &LateContext<'_>, local: &'_ Local<'_>) {
-        if_chain! {
-            if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, local.hir_id);
-            if !in_external_macro(cx.tcx.sess, local.span);
-            if let Some(init) = local.init;
-            then {
-                self.visit_expr(init);
-
-                if self.local_level > 0 {
-                    self.local_span = Some(local.span);
-                }
-            }
+            span_lint_and_help(
+                cx,
+                UNDOCUMENTED_UNSAFE_BLOCKS,
+                span,
+                "unsafe block missing a safety comment",
+                None,
+                "consider adding a safety comment on the preceding line",
+            );
         }
     }
+}
 
-    fn check_block_post(&mut self, _: &LateContext<'_>, _: &'_ Block<'_>) {
-        self.local_level = self.local_level.saturating_sub(1);
-
-        if self.local_level == 0 {
-            self.local_checked = false;
-            self.local_span = None;
-        }
-    }
+fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, block: &Block<'_>) -> bool {
+    let source_map = cx.sess().source_map();
+    let file_pos = source_map.lookup_byte_offset(block.span.lo());
+    file_pos
+        .sf
+        .src
+        .as_deref()
+        .and_then(|src| src.get(file_pos.pos.to_usize()..))
+        .map_or(true, |src| !src.starts_with("unsafe"))
 }
 
-impl<'v> Visitor<'v> for UndocumentedUnsafeBlocks {
-    fn visit_expr(&mut self, ex: &'v Expr<'v>) {
-        match ex.kind {
-            ExprKind::Block(_, _) => self.local_level = self.local_level.saturating_add(1),
-            _ => walk_expr(self, ex),
+/// Checks if the lines immediately preceding the block contain a safety comment.
+fn block_has_safety_comment(cx: &LateContext<'_>, block: &Block<'_>) -> bool {
+    // This intentionally ignores text before the start of a function so something like:
+    // ```
+    //     // SAFETY: reason
+    //     fn foo() { unsafe { .. } }
+    // ```
+    // won't work. This is to avoid dealing with where such a comment should be place relative to
+    // attributes and doc comments.
+
+    let source_map = cx.sess().source_map();
+    let ctxt = block.span.ctxt();
+    if ctxt != SyntaxContext::root() {
+        // From a macro expansion. Get the text from the start of the macro declaration to start of the unsafe block.
+        //     macro_rules! foo { () => { stuff }; (x) => { unsafe { stuff } }; }
+        //     ^--------------------------------------------^
+        if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo())
+            && let Ok(macro_line) = source_map.lookup_line(ctxt.outer_expn_data().def_site.lo())
+            && Lrc::ptr_eq(&unsafe_line.sf, &macro_line.sf)
+            && let Some(src) = unsafe_line.sf.src.as_deref()
+        {
+            macro_line.line < unsafe_line.line && text_has_safety_comment(
+                src,
+                &unsafe_line.sf.lines[macro_line.line + 1..=unsafe_line.line],
+                unsafe_line.sf.start_pos.to_usize(),
+            )
+        } else {
+            // Problem getting source text. Pretend a comment was found.
+            true
         }
+    } else if let Ok(unsafe_line) = source_map.lookup_line(block.span.lo())
+        && let Some(body) = cx.enclosing_body
+        && let Some(body_span) = walk_span_to_context(cx.tcx.hir().body(body).value.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()
+    {
+        // Get the text from the start of function body to the unsafe block.
+        //     fn foo() { some_stuff; unsafe { stuff }; other_stuff; }
+        //              ^-------------^
+        body_line.line < unsafe_line.line && text_has_safety_comment(
+            src,
+            &unsafe_line.sf.lines[body_line.line + 1..=unsafe_line.line],
+            unsafe_line.sf.start_pos.to_usize(),
+        )
+    } else {
+        // Problem getting source text. Pretend a comment was found.
+        true
     }
 }
 
-impl UndocumentedUnsafeBlocks {
-    fn block_has_safety_comment(&mut self, tcx: TyCtxt<'_>, enclosing_hir_id: HirId, block_span: Span) -> Option<bool> {
-        let map = tcx.hir();
-        let source_map = tcx.sess.source_map();
-
-        let enclosing_scope_span = map.opt_span(enclosing_hir_id)?;
-
-        let between_span = if block_span.from_expansion() {
-            self.macro_expansion = true;
-            enclosing_scope_span.with_hi(block_span.hi()).source_callsite()
-        } else {
-            self.macro_expansion = false;
-            enclosing_scope_span.to(block_span).source_callsite()
-        };
-
-        let file_name = source_map.span_to_filename(between_span);
-        let source_file = source_map.get_source_file(&file_name)?;
-
-        let lex_start = (between_span.lo().0 - source_file.start_pos.0 + 1) as usize;
-        let lex_end = (between_span.hi().0 - source_file.start_pos.0) as usize;
-        let src_str = source_file.src.as_ref()?[lex_start..lex_end].to_string();
-
-        let source_start_pos = source_file.start_pos.0 as usize + lex_start;
-
-        let mut pos = 0;
-        let mut comment = false;
-
-        for token in rustc_lexer::tokenize(&src_str) {
-            match token.kind {
-                TokenKind::LineComment { doc_style: None }
-                | TokenKind::BlockComment {
-                    doc_style: None,
-                    terminated: true,
-                } => {
-                    let comment_str = src_str[pos + 2..pos + token.len].to_ascii_uppercase();
-
-                    if comment_str.contains("SAFETY:") {
-                        comment = true;
-                    }
-                },
-                // We need to add all whitespace to `pos` before checking the comment's line number
-                TokenKind::Whitespace => {},
-                _ => {
-                    if comment {
-                        // Get the line number of the "comment" (really wherever the trailing whitespace ended)
-                        let comment_line_num = source_file
-                            .lookup_file_pos(BytePos((source_start_pos + pos).try_into().unwrap()))
-                            .0;
-                        // Find the block/local's line number
-                        let block_line_num = tcx.sess.source_map().lookup_char_pos(block_span.lo()).line;
-
-                        // Check the comment is immediately followed by the block/local
-                        if block_line_num == comment_line_num + 1 || block_line_num == comment_line_num {
-                            return Some(true);
-                        }
-
-                        comment = false;
-                    }
-                },
+/// Checks if the given text has a safety comment for the immediately proceeding line.
+fn text_has_safety_comment(src: &str, line_starts: &[BytePos], offset: usize) -> bool {
+    let mut lines = line_starts
+        .array_windows::<2>()
+        .rev()
+        .map_while(|[start, end]| {
+            src.get(start.to_usize() - offset..end.to_usize() - offset)
+                .map(|text| (start.to_usize(), text.trim_start()))
+        })
+        .filter(|(_, text)| !text.is_empty());
+
+    let Some((line_start, line)) = lines.next() else {
+        return false;
+    };
+    // Check for a sequence of line comments.
+    if line.starts_with("//") {
+        let mut line = line;
+        loop {
+            if line.to_ascii_uppercase().contains("SAFETY:") {
+                return true;
+            }
+            match lines.next() {
+                Some((_, x)) if x.starts_with("//") => line = x,
+                _ => return false,
             }
-
-            pos += token.len;
         }
-
-        Some(false)
     }
-
-    fn lint(&self, cx: &LateContext<'_>, mut span: Span) {
-        let source_map = cx.tcx.sess.source_map();
-
-        if source_map.is_multiline(span) {
-            span = source_map.span_until_char(span, '\n');
+    // No line comments; look for the start of a block comment.
+    // This will only find them if they are at the start of a line.
+    let (mut line_start, mut line) = (line_start, line);
+    loop {
+        if line.starts_with("/*") {
+            let src = src[line_start..line_starts.last().unwrap().to_usize()].trim_start();
+            let mut tokens = tokenize(src);
+            return src[..tokens.next().unwrap().len]
+                .to_ascii_uppercase()
+                .contains("SAFETY:")
+                && tokens.all(|t| t.kind == TokenKind::Whitespace);
         }
-
-        if self.macro_expansion {
-            span_lint_and_help(
-                cx,
-                UNDOCUMENTED_UNSAFE_BLOCKS,
-                span,
-                "unsafe block in macro expansion missing a safety comment",
-                None,
-                "consider adding a safety comment in the macro definition",
-            );
-        } else {
-            let block_indent = indent_of(cx, span);
-            let suggestion = format!("// SAFETY: ...\n{}", snippet(cx, span, ".."));
-
-            span_lint_and_sugg(
-                cx,
-                UNDOCUMENTED_UNSAFE_BLOCKS,
-                span,
-                "unsafe block missing a safety comment",
-                "consider adding a safety comment",
-                reindent_multiline(Cow::Borrowed(&suggestion), true, block_indent).to_string(),
-                Applicability::HasPlaceholders,
-            );
+        match lines.next() {
+            Some(x) => (line_start, line) = x,
+            None => return false,
         }
     }
 }
diff --git a/clippy_lints/src/undropped_manually_drops.rs b/clippy_lints/src/undropped_manually_drops.rs
deleted file mode 100644 (file)
index db65276..0000000
+++ /dev/null
@@ -1,59 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::path_res;
-use clippy_utils::ty::is_type_lang_item;
-use rustc_hir::{lang_items, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Prevents the safe `std::mem::drop` function from being called on `std::mem::ManuallyDrop`.
-    ///
-    /// ### Why is this bad?
-    /// The safe `drop` function does not drop the inner value of a `ManuallyDrop`.
-    ///
-    /// ### Known problems
-    /// Does not catch cases if the user binds `std::mem::drop`
-    /// to a different name and calls it that way.
-    ///
-    /// ### Example
-    /// ```rust
-    /// struct S;
-    /// drop(std::mem::ManuallyDrop::new(S));
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// struct S;
-    /// unsafe {
-    ///     std::mem::ManuallyDrop::drop(&mut std::mem::ManuallyDrop::new(S));
-    /// }
-    /// ```
-    #[clippy::version = "1.49.0"]
-    pub UNDROPPED_MANUALLY_DROPS,
-    correctness,
-    "use of safe `std::mem::drop` function to drop a std::mem::ManuallyDrop, which will not drop the inner value"
-}
-
-declare_lint_pass!(UndroppedManuallyDrops => [UNDROPPED_MANUALLY_DROPS]);
-
-impl<'tcx> LateLintPass<'tcx> for UndroppedManuallyDrops {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            if let ExprKind::Call(fun, [arg_0, ..]) = expr.kind;
-            if path_res(cx, fun).opt_def_id() == cx.tcx.get_diagnostic_item(sym::mem_drop);
-            let ty = cx.typeck_results().expr_ty(arg_0);
-            if is_type_lang_item(cx, ty, lang_items::LangItem::ManuallyDrop);
-            then {
-                span_lint_and_help(
-                    cx,
-                    UNDROPPED_MANUALLY_DROPS,
-                    expr.span,
-                    "the inner value of this ManuallyDrop will not be dropped",
-                    None,
-                    "to drop a `ManuallyDrop<T>`, use std::mem::ManuallyDrop::drop",
-                );
-            }
-        }
-    }
-}
index 09d671e11184d6e2e456909b3326af74b6ce3c04..f8e1021af0ea11b9bd46acc78dfe62ea9ea4ccaf 100644 (file)
@@ -34,7 +34,7 @@
     ///
     /// ### Example
     /// ```rust
-    /// struct Foo {}
+    /// struct Foo;
     /// impl Foo {
     ///     fn new() -> Foo {
     ///         Foo {}
@@ -43,7 +43,7 @@
     /// ```
     /// could be
     /// ```rust
-    /// struct Foo {}
+    /// struct Foo;
     /// impl Foo {
     ///     fn new() -> Self {
     ///         Self {}
index 680b2eb1da723d7696878d7d9f1ed2d27f69ebc7..271c3a3dd181cef4db12b84a6db0f9a3fa42ebf5 100644 (file)
@@ -156,7 +156,7 @@ pub(crate) fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
     ///
     /// Suppress lints whenever the suggested change would cause breakage for other crates.
     (avoid_breaking_exported_api: bool = true),
-    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS.
+    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED.
     ///
     /// The minimum rust version that the project supports
     (msrv: Option<String> = None),
index b3b241392fed5d172d7f2649236feeb0318d352d..25d74b8c49939da8a643e60a2da1e4b264440d91 100644 (file)
@@ -25,7 +25,7 @@
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::mir::interpret::ConstValue;
-use rustc_middle::ty::{self, subst::GenericArgKind};
+use rustc_middle::ty::{self, fast_reject::SimplifiedTypeGen, subst::GenericArgKind, FloatTy};
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::Spanned;
@@ -889,7 +889,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve
                     }
                 }
             },
-            Res::Def(DefKind::Const | DefKind::Static, def_id) => {
+            Res::Def(DefKind::Const | DefKind::Static(..), def_id) => {
                 if let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(def_id) {
                     if let ItemKind::Const(.., body_id) | ItemKind::Static(.., body_id) = item.kind {
                         let body = cx.tcx.hir().body(body_id);
@@ -934,7 +934,16 @@ pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
     // implementations of native types. Check lang items.
     let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
     let lang_items = cx.tcx.lang_items();
-    for item_def_id in lang_items.items().iter().flatten() {
+    // This list isn't complete, but good enough for our current list of paths.
+    let incoherent_impls = [
+        SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F32),
+        SimplifiedTypeGen::FloatSimplifiedType(FloatTy::F64),
+        SimplifiedTypeGen::SliceSimplifiedType,
+        SimplifiedTypeGen::StrSimplifiedType,
+    ]
+    .iter()
+    .flat_map(|&ty| cx.tcx.incoherent_impls(ty));
+    for item_def_id in lang_items.items().iter().flatten().chain(incoherent_impls) {
         let lang_item_path = cx.get_def_path(*item_def_id);
         if path_syms.starts_with(&lang_item_path) {
             if let [item] = &path_syms[lang_item_path.len()..] {
index b3fad6ce7b65137e8a0a6a8e0c0c6fd2210c9c88..ca03b8010dd821c2c68da237efae631c279e1711 100644 (file)
@@ -756,7 +756,7 @@ fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
             let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
             if match_type(self.cx, expr_ty, &paths::LINT);
             then {
-                if let hir::def::Res::Def(DefKind::Static, _) = path.res {
+                if let hir::def::Res::Def(DefKind::Static(..), _) = path.res {
                     let lint_name = last_path_segment(qpath).ident.name;
                     self.lints.push(sym_to_string(lint_name).to_ascii_lowercase());
                 } else if let Some(local) = get_parent_local(self.cx, expr) {
index d3ed8da4499f88ee3aec2578e4983ffb1516dc51..0b1fd95c3453d1ba4f6e5e653d47488ac3f1b1ab 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_utils"
-version = "0.1.61"
+version = "0.1.62"
 edition = "2021"
 publish = false
 
index 62e144398012d4b5780c83bbe51f44a2efc5928e..a275bac4ce63dc5436ea18d4677f8c219c50488c 100644 (file)
 use rustc_hir::itemlikevisit::ItemLikeVisitor;
 use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
 use rustc_hir::{
-    def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr,
-    ExprKind, FnDecl, ForeignItem, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local,
-    MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
-    TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
+    def, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr, ExprKind, FnDecl,
+    ForeignItem, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local, MatchSource,
+    Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind,
+    TraitRef, TyKind, UnOp,
 };
 use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::place::PlaceBase;
 use rustc_middle::ty as rustc_ty;
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 use rustc_middle::ty::binding::BindingMode;
-use rustc_middle::ty::{IntTy, UintTy, FloatTy};
-use rustc_middle::ty::fast_reject::SimplifiedTypeGen::*;
+use rustc_middle::ty::fast_reject::SimplifiedTypeGen::{
+    ArraySimplifiedType, BoolSimplifiedType, CharSimplifiedType, FloatSimplifiedType, IntSimplifiedType,
+    PtrSimplifiedType, SliceSimplifiedType, StrSimplifiedType, UintSimplifiedType,
+};
 use rustc_middle::ty::{layout::IntegerExt, BorrowKind, DefIdTree, Ty, TyCtxt, TypeAndMut, TypeFoldable, UpvarCapture};
+use rustc_middle::ty::{FloatTy, IntTy, UintTy};
 use rustc_semver::RustcVersion;
 use rustc_session::Session;
 use rustc_span::hygiene::{ExpnKind, MacroKind};
@@ -522,7 +525,7 @@ fn find_crate(tcx: TyCtxt<'_>, name: &str) -> Option<DefId> {
     let tcx = cx.tcx;
     let starts = find_primitive(tcx, base)
         .chain(find_crate(tcx, base))
-        .flat_map(|id| item_child_by_name(tcx, id, first));
+        .filter_map(|id| item_child_by_name(tcx, id, first));
 
     for first in starts {
         let last = path
index fce93153d96ec1841eae99b5ec3b584e22c2e261..0424e06720263e5322b38eabf54d518bb899d0e6 100644 (file)
@@ -14,7 +14,7 @@ macro_rules! msrv_aliases {
 msrv_aliases! {
     1,53,0 { OR_PATTERNS, MANUAL_BITS }
     1,52,0 { STR_SPLIT_ONCE }
-    1,51,0 { BORROW_AS_PTR }
+    1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS }
     1,50,0 { BOOL_THEN }
     1,47,0 { TAU }
     1,46,0 { CONST_IF_MATCH }
@@ -30,6 +30,6 @@ macro_rules! msrv_aliases {
     1,34,0 { TRY_FROM }
     1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
     1,28,0 { FROM_BOOL }
-    1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST }
+    1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
     1,16,0 { STR_REPEAT }
 }
index 6f56f8d51365a9407e2a36ce6205ac6a1719c789..79e6e92dc0aaf3a2506db51edaeaf6cc47fcb1df 100644 (file)
 pub const PTR_READ_VOLATILE: [&str; 3] = ["core", "ptr", "read_volatile"];
 pub const PTR_REPLACE: [&str; 3] = ["core", "ptr", "replace"];
 pub const PTR_SWAP: [&str; 3] = ["core", "ptr", "swap"];
+pub const PTR_UNALIGNED_VOLATILE_LOAD: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_load"];
+pub const PTR_UNALIGNED_VOLATILE_STORE: [&str; 3] = ["core", "intrinsics", "unaligned_volatile_store"];
 pub const PTR_WRITE: [&str; 3] = ["core", "ptr", "write"];
 pub const PTR_WRITE_BYTES: [&str; 3] = ["core", "intrinsics", "write_bytes"];
 pub const PTR_WRITE_UNALIGNED: [&str; 3] = ["core", "ptr", "write_unaligned"];
index afe3033c288cffd8d95b06ea2e833989455cb81a..c4f8f989384280659f899f6f6d9820cf0087e8a6 100644 (file)
@@ -121,4 +121,25 @@ happened a stable backport, make sure to re-merge those changes just as with the
 
 For this see the document on [how to update the changelog].
 
+If you don't have time to do a complete changelog update right away, just update
+the following parts:
+
+- Remove the `(beta)` from the new stable version:
+
+  ```markdown
+  ## Rust 1.XX (beta) -> ## Rust 1.XX
+  ```
+
+- Update the release date line of the new stable version:
+
+  ```markdown
+  Current beta, release 20YY-MM-DD -> Current stable, released 20YY-MM-DD
+  ```
+
+- Update the release date line of the previous stable version:
+
+  ```markdown
+  Current stable, released 20YY-MM-DD -> Released 20YY-MM-DD
+  ```
+
 [how to update the changelog]: https://github.com/rust-lang/rust-clippy/blob/master/doc/changelog_update.md
index 5befb856a023470c0e211f281807f65a6fa17fa3..bb29c71e9f455889ff8cb1f001ee916fb273b0c1 100644 (file)
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2022-03-24"
+channel = "nightly-2022-04-07"
 components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
index bc1b0d7457559da68ddfdf3f964ed6f59018551e..00dc916b217ca0b93f18a387290545e517a2a97f 100644 (file)
@@ -165,8 +165,7 @@ fn report_clippy_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
     // Separate the output with an empty line
     eprintln!();
 
-    let fallback_bundle = rustc_errors::fallback_fluent_bundle(false)
-        .expect("failed to load fallback fluent bundle");
+    let fallback_bundle = rustc_errors::fallback_fluent_bundle(false).expect("failed to load fallback fluent bundle");
     let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
         rustc_errors::ColorConfig::Auto,
         None,
diff --git a/tests/check-fmt.rs b/tests/check-fmt.rs
new file mode 100644 (file)
index 0000000..0defd45
--- /dev/null
@@ -0,0 +1,28 @@
+#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![warn(rust_2018_idioms, unused_lifetimes)]
+
+use std::path::PathBuf;
+use std::process::Command;
+
+#[test]
+fn fmt() {
+    if option_env!("RUSTC_TEST_SUITE").is_some() || option_env!("NO_FMT_TEST").is_some() {
+        return;
+    }
+
+    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
+    let output = Command::new("cargo")
+        .current_dir(root_dir)
+        .args(&["dev", "fmt", "--check"])
+        .output()
+        .unwrap();
+
+    println!("status: {}", output.status);
+    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
+    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
+
+    assert!(
+        output.status.success(),
+        "Formatting check failed. Run `cargo dev fmt` to update formatting."
+    );
+}
diff --git a/tests/fmt.rs b/tests/fmt.rs
deleted file mode 100644 (file)
index 0defd45..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
-#![warn(rust_2018_idioms, unused_lifetimes)]
-
-use std::path::PathBuf;
-use std::process::Command;
-
-#[test]
-fn fmt() {
-    if option_env!("RUSTC_TEST_SUITE").is_some() || option_env!("NO_FMT_TEST").is_some() {
-        return;
-    }
-
-    let root_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
-    let output = Command::new("cargo")
-        .current_dir(root_dir)
-        .args(&["dev", "fmt", "--check"])
-        .output()
-        .unwrap();
-
-    println!("status: {}", output.status);
-    println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
-    println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
-
-    assert!(
-        output.status.success(),
-        "Formatting check failed. Run `cargo dev fmt` to update formatting."
-    );
-}
index af4c298b310852bf997a072838f58a71efcfe3fb..e2010e9981315a748fbdbebc0a902bfa479766cb 100644 (file)
@@ -1,19 +1,19 @@
-error: `mod.rs` files are required, found `/bad/inner.rs`
+error: `mod.rs` files are required, found `bad/inner.rs`
   --> $DIR/bad/inner.rs:1:1
    |
 LL | pub mod stuff;
    | ^
    |
    = note: `-D clippy::self-named-module-files` implied by `-D warnings`
-   = help: move `/bad/inner.rs` to `/bad/inner/mod.rs`
+   = help: move `bad/inner.rs` to `bad/inner/mod.rs`
 
-error: `mod.rs` files are required, found `/bad/inner/stuff.rs`
+error: `mod.rs` files are required, found `bad/inner/stuff.rs`
   --> $DIR/bad/inner/stuff.rs:1:1
    |
 LL | pub mod most;
    | ^
    |
-   = help: move `/bad/inner/stuff.rs` to `/bad/inner/stuff/mod.rs`
+   = help: move `bad/inner/stuff.rs` to `bad/inner/stuff/mod.rs`
 
 error: aborting due to 2 previous errors
 
index 11e15db7fb96b47829251b95126dae9af8791466..f91940209383ff66d330c58df0e1ea54ca3d83b4 100644 (file)
@@ -1,11 +1,11 @@
-error: `mod.rs` files are not allowed, found `/bad/mod.rs`
+error: `mod.rs` files are not allowed, found `bad/mod.rs`
   --> $DIR/bad/mod.rs:1:1
    |
 LL | pub struct Thing;
    | ^
    |
    = note: `-D clippy::mod-module-files` implied by `-D warnings`
-   = help: move `/bad/mod.rs` to `/bad.rs`
+   = help: move `bad/mod.rs` to `bad.rs`
 
 error: aborting due to previous error
 
index 9302e02ccb9c4f1a203dca9d0c4ad19cd430f919..67e1a07b7f5fac8a895d8c224599facaa0225516 100644 (file)
@@ -46,11 +46,6 @@ LL | |     report_in_external_macro: true
 LL | | }
    | |_^
    |
-note: the lint level is defined here
-  --> $DIR/check_clippy_version_attribute.rs:1:9
-   |
-LL | #![deny(clippy::internal)]
-   |         ^^^^^^^^^^^^^^^^
    = note: `#[deny(clippy::missing_clippy_version_attribute)]` implied by `#[deny(clippy::internal)]`
    = help: please use a `clippy::version` attribute, see `doc/adding_lints.md`
    = note: this error originates in the macro `$crate::declare_tool_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
index 242984680e163bd9eef1de3b132ffe93c7663c73..32dd80246fab44cda2884f4a4f996d1e8f69d3e9 100644 (file)
@@ -4,6 +4,6 @@ struct S {
     a: bool,
 }
 
-struct Foo {}
+struct Foo;
 
 fn main() {}
diff --git a/tests/ui/auxiliary/proc_macro_unsafe.rs b/tests/ui/auxiliary/proc_macro_unsafe.rs
new file mode 100644 (file)
index 0000000..3c40f77
--- /dev/null
@@ -0,0 +1,18 @@
+// compile-flags: --emit=link
+// no-prefer-dynamic
+
+#![crate_type = "proc-macro"]
+
+extern crate proc_macro;
+
+use proc_macro::{Delimiter, Group, Ident, TokenStream, TokenTree};
+
+#[proc_macro]
+pub fn unsafe_block(input: TokenStream) -> TokenStream {
+    let span = input.into_iter().next().unwrap().span();
+    TokenStream::from_iter([TokenTree::Ident(Ident::new("unsafe", span)), {
+        let mut group = Group::new(Delimiter::Brace, TokenStream::new());
+        group.set_span(span);
+        TokenTree::Group(group)
+    }])
+}
index 8a7afa934502d101c33d81e1e8d8f26ff435fa27..9851d4791d8db59da4c1bcf333b770e7148f5119 100644 (file)
@@ -1,4 +1,4 @@
-error: called `.byte().nth()` on a `String`
+error: called `.bytes().nth()` on a `String`
   --> $DIR/bytes_nth.rs:8:13
    |
 LL |     let _ = s.bytes().nth(3);
@@ -6,13 +6,13 @@ LL |     let _ = s.bytes().nth(3);
    |
    = note: `-D clippy::bytes-nth` implied by `-D warnings`
 
-error: called `.byte().nth()` on a `String`
+error: called `.bytes().nth()` on a `String`
   --> $DIR/bytes_nth.rs:9:14
    |
 LL |     let _ = &s.bytes().nth(3);
    |              ^^^^^^^^^^^^^^^^ help: try: `s.as_bytes().get(3)`
 
-error: called `.byte().nth()` on a `str`
+error: called `.bytes().nth()` on a `str`
   --> $DIR/bytes_nth.rs:10:13
    |
 LL |     let _ = s[..].bytes().nth(3);
index 68719c2bc6d05ea1afa005e00caee7f3c83b2418..0d65071af15ed1c4694daec5b20dac9baa96e56a 100644 (file)
@@ -2,7 +2,7 @@
 
 use std::string::String;
 
-struct TestStruct {}
+struct TestStruct;
 
 impl TestStruct {
     fn ends_with(self, arg: &str) {}
index 2e31ad3172ee8cef1a5271c27c2fd8367b7d417c..cf85a5ca931dd1e560134cf39304659254af3b43 100644 (file)
@@ -7,7 +7,7 @@
     clippy::cast_sign_loss,
     clippy::cast_possible_wrap
 )]
-#[allow(clippy::no_effect, clippy::unnecessary_operation)]
+#[allow(clippy::cast_abs_to_unsigned, clippy::no_effect, clippy::unnecessary_operation)]
 fn main() {
     // Test clippy::cast_precision_loss
     let x0 = 1i32;
diff --git a/tests/ui/cast_abs_to_unsigned.fixed b/tests/ui/cast_abs_to_unsigned.fixed
new file mode 100644 (file)
index 0000000..4ec2465
--- /dev/null
@@ -0,0 +1,8 @@
+// run-rustfix
+#![warn(clippy::cast_abs_to_unsigned)]
+
+fn main() {
+    let x: i32 = -42;
+    let y: u32 = x.unsigned_abs();
+    println!("The absolute value of {} is {}", x, y);
+}
diff --git a/tests/ui/cast_abs_to_unsigned.rs b/tests/ui/cast_abs_to_unsigned.rs
new file mode 100644 (file)
index 0000000..59b9c8c
--- /dev/null
@@ -0,0 +1,8 @@
+// run-rustfix
+#![warn(clippy::cast_abs_to_unsigned)]
+
+fn main() {
+    let x: i32 = -42;
+    let y: u32 = x.abs() as u32;
+    println!("The absolute value of {} is {}", x, y);
+}
diff --git a/tests/ui/cast_abs_to_unsigned.stderr b/tests/ui/cast_abs_to_unsigned.stderr
new file mode 100644 (file)
index 0000000..eb12857
--- /dev/null
@@ -0,0 +1,10 @@
+error: casting the result of `i32::abs()` to u32
+  --> $DIR/cast_abs_to_unsigned.rs:6:18
+   |
+LL |     let y: u32 = x.abs() as u32;
+   |                  ^^^^^^^^^^^^^^ help: replace with: `x.unsigned_abs()`
+   |
+   = note: `-D clippy::cast-abs-to-unsigned` implied by `-D warnings`
+
+error: aborting due to previous error
+
index 659591fffbecdb56993cdf8cbd8676430ec52e8c..e4e7290a30e9e711c911c34d96a1239af8e0d19a 100644 (file)
@@ -1,6 +1,7 @@
 //! Test casts for alignment issues
 
 #![feature(rustc_private)]
+#![feature(core_intrinsics)]
 extern crate libc;
 
 #[warn(clippy::cast_ptr_alignment)]
@@ -34,4 +35,17 @@ fn main() {
     (&1u32 as *const u32 as *const libc::c_void) as *const u32;
     // For ZST, we should trust the user. See #4256
     (&1u32 as *const u32 as *const ()) as *const u32;
+
+    // Issue #2881
+    let mut data = [0u8, 0u8];
+    unsafe {
+        let ptr = &data as *const [u8; 2] as *const u8;
+        let _ = (ptr as *const u16).read_unaligned();
+        let _ = core::ptr::read_unaligned(ptr as *const u16);
+        let _ = core::intrinsics::unaligned_volatile_load(ptr as *const u16);
+        let ptr = &mut data as *mut [u8; 2] as *mut u8;
+        let _ = (ptr as *mut u16).write_unaligned(0);
+        let _ = core::ptr::write_unaligned(ptr as *mut u16, 0);
+        let _ = core::intrinsics::unaligned_volatile_store(ptr as *mut u16, 0);
+    }
 }
index aedd368445554b05ad2d1744be82e682081534d4..5df2b5b1094be2b96bd552184326695820fb6b6d 100644 (file)
@@ -1,5 +1,5 @@
 error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes)
-  --> $DIR/cast_alignment.rs:18:5
+  --> $DIR/cast_alignment.rs:19:5
    |
 LL |     (&1u8 as *const u8) as *const u16;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -7,19 +7,19 @@ LL |     (&1u8 as *const u8) as *const u16;
    = note: `-D clippy::cast-ptr-alignment` implied by `-D warnings`
 
 error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes)
-  --> $DIR/cast_alignment.rs:19:5
+  --> $DIR/cast_alignment.rs:20:5
    |
 LL |     (&mut 1u8 as *mut u8) as *mut u16;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting from `*const u8` to a more-strictly-aligned pointer (`*const u16`) (1 < 2 bytes)
-  --> $DIR/cast_alignment.rs:22:5
+  --> $DIR/cast_alignment.rs:23:5
    |
 LL |     (&1u8 as *const u8).cast::<u16>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: casting from `*mut u8` to a more-strictly-aligned pointer (`*mut u16`) (1 < 2 bytes)
-  --> $DIR/cast_alignment.rs:23:5
+  --> $DIR/cast_alignment.rs:24:5
    |
 LL |     (&mut 1u8 as *mut u8).cast::<u16>();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index d44b0fae82001b3824b2a83c8d44e74eddacb0d8..88cfa1f923c0bff23329871fa193c2698e3c65b5 100644 (file)
@@ -8,7 +8,7 @@ pub struct Bar {
 }
 
 #[derive(Eq, PartialEq, Debug, Hash)]
-pub struct Foo {}
+pub struct Foo;
 
 #[allow(clippy::implicit_hasher)]
 // This should not cause a "cannot relate bound region" ICE.
index 8d9a1af8ff1180b50e824588b55bbcddc0cb93ea..4fe92d356c44de5f2d2f24f2292d6135b8b4aa9f 100644 (file)
@@ -4,7 +4,7 @@
 #![warn(clippy::use_self)]
 #![allow(dead_code)]
 
-struct Foo {}
+struct Foo;
 
 impl Foo {
     fn new() -> Self {
index 0e2ab1a39b82f98c0faefe4187c36f3c52b5a78f..9cbafc716b5000dc2bd4ac51904fa90369df037a 100644 (file)
@@ -7,7 +7,7 @@ trait Trait {
     fn broken() -> Self::Ty;
 }
 
-struct Foo {}
+struct Foo;
 
 impl Trait for Foo {
     type Ty = Foo;
index 111350a6280dc44a4415e30f74fe636f673c0f0e..1a33e647588f19ea3c6c678285c23ae9e483b57c 100644 (file)
@@ -5,11 +5,7 @@ LL |     unsafe { 0 };
    |     ^^^^^^^^^^^^
    |
    = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
-help: consider adding a safety comment
-   |
-LL ~     // SAFETY: ...
-LL ~     unsafe { 0 };
-   |
+   = help: consider adding a safety comment on the preceding line
 
 error: aborting due to previous error
 
index 676564b2445d506185bb5d9bafe68c15ab3538a9..376ff97ba6036f3fbbf6336504a73610b910dc4f 100644 (file)
@@ -3,7 +3,7 @@
 
 trait Foo {}
 
-struct Bar {}
+struct Bar;
 
 struct Baz<'a> {
     bar: &'a Bar,
index a41bcb33b4460bf386f714057c377e50ea54ba14..6f9d98bbfe7f341b8b166800314b77495ba0697a 100644 (file)
@@ -6,6 +6,6 @@ pub fn foo(bar: *const u8) {
 
 // Regression test for https://github.com/rust-lang/rust-clippy/issues/4917
 /// <foo
-struct A {}
+struct A;
 
 fn main() {}
diff --git a/tests/ui/crate_in_macro_def.fixed b/tests/ui/crate_in_macro_def.fixed
new file mode 100644 (file)
index 0000000..9fc594b
--- /dev/null
@@ -0,0 +1,56 @@
+// run-rustfix
+#![warn(clippy::crate_in_macro_def)]
+
+mod hygienic {
+    #[macro_export]
+    macro_rules! print_message_hygienic {
+        () => {
+            println!("{}", $crate::hygienic::MESSAGE);
+        };
+    }
+
+    pub const MESSAGE: &str = "Hello!";
+}
+
+mod unhygienic {
+    #[macro_export]
+    macro_rules! print_message_unhygienic {
+        () => {
+            println!("{}", $crate::unhygienic::MESSAGE);
+        };
+    }
+
+    pub const MESSAGE: &str = "Hello!";
+}
+
+mod unhygienic_intentionally {
+    // For cases where the use of `crate` is intentional, applying `allow` to the macro definition
+    // should suppress the lint.
+    #[allow(clippy::crate_in_macro_def)]
+    #[macro_export]
+    macro_rules! print_message_unhygienic_intentionally {
+        () => {
+            println!("{}", crate::CALLER_PROVIDED_MESSAGE);
+        };
+    }
+}
+
+#[macro_use]
+mod not_exported {
+    macro_rules! print_message_not_exported {
+        () => {
+            println!("{}", crate::not_exported::MESSAGE);
+        };
+    }
+
+    pub const MESSAGE: &str = "Hello!";
+}
+
+fn main() {
+    print_message_hygienic!();
+    print_message_unhygienic!();
+    print_message_unhygienic_intentionally!();
+    print_message_not_exported!();
+}
+
+pub const CALLER_PROVIDED_MESSAGE: &str = "Hello!";
diff --git a/tests/ui/crate_in_macro_def.rs b/tests/ui/crate_in_macro_def.rs
new file mode 100644 (file)
index 0000000..ac45610
--- /dev/null
@@ -0,0 +1,56 @@
+// run-rustfix
+#![warn(clippy::crate_in_macro_def)]
+
+mod hygienic {
+    #[macro_export]
+    macro_rules! print_message_hygienic {
+        () => {
+            println!("{}", $crate::hygienic::MESSAGE);
+        };
+    }
+
+    pub const MESSAGE: &str = "Hello!";
+}
+
+mod unhygienic {
+    #[macro_export]
+    macro_rules! print_message_unhygienic {
+        () => {
+            println!("{}", crate::unhygienic::MESSAGE);
+        };
+    }
+
+    pub const MESSAGE: &str = "Hello!";
+}
+
+mod unhygienic_intentionally {
+    // For cases where the use of `crate` is intentional, applying `allow` to the macro definition
+    // should suppress the lint.
+    #[allow(clippy::crate_in_macro_def)]
+    #[macro_export]
+    macro_rules! print_message_unhygienic_intentionally {
+        () => {
+            println!("{}", crate::CALLER_PROVIDED_MESSAGE);
+        };
+    }
+}
+
+#[macro_use]
+mod not_exported {
+    macro_rules! print_message_not_exported {
+        () => {
+            println!("{}", crate::not_exported::MESSAGE);
+        };
+    }
+
+    pub const MESSAGE: &str = "Hello!";
+}
+
+fn main() {
+    print_message_hygienic!();
+    print_message_unhygienic!();
+    print_message_unhygienic_intentionally!();
+    print_message_not_exported!();
+}
+
+pub const CALLER_PROVIDED_MESSAGE: &str = "Hello!";
diff --git a/tests/ui/crate_in_macro_def.stderr b/tests/ui/crate_in_macro_def.stderr
new file mode 100644 (file)
index 0000000..9ac5937
--- /dev/null
@@ -0,0 +1,10 @@
+error: `crate` references the macro call's crate
+  --> $DIR/crate_in_macro_def.rs:19:28
+   |
+LL |             println!("{}", crate::unhygienic::MESSAGE);
+   |                            ^^^^^ help: to reference the macro definition's crate, use: `$crate`
+   |
+   = note: `-D clippy::crate-in-macro-def` implied by `-D warnings`
+
+error: aborting due to previous error
+
index 1b0e7544e79c6289f17b6d48b9f1a0dc7c51e579..e0b4a2f6942392c575e3f491914dfbf11b7cf6cd 100644 (file)
@@ -134,7 +134,7 @@ mod enum_ctor {
 }
 
 mod method_calls {
-    struct StructForMethodCallTest {}
+    struct StructForMethodCallTest;
 
     impl StructForMethodCallTest {
         fn concrete_arg(&self, f: f64) {}
index e9687777bbd0bed95d74767187deb9566e5e8167..50bbb6eec6c700fdbdb7a54051e48c21715b0dbd 100644 (file)
@@ -134,7 +134,7 @@ fn test() {
 }
 
 mod method_calls {
-    struct StructForMethodCallTest {}
+    struct StructForMethodCallTest;
 
     impl StructForMethodCallTest {
         fn concrete_arg(&self, f: f64) {}
index 55c082fcb19fb7f1b563539721f9a89b111ba14b..bded9e2c0e801723b89547d606120a989078473d 100644 (file)
@@ -133,7 +133,7 @@ mod enum_ctor {
 }
 
 mod method_calls {
-    struct StructForMethodCallTest {}
+    struct StructForMethodCallTest;
 
     impl StructForMethodCallTest {
         fn concrete_arg(&self, x: i32) {}
index e0a4828ce9ff636aed3c8c510e6e094b9f370826..3fceefa551c7843c4d1b54a36f98c3ac86bf2612 100644 (file)
@@ -133,7 +133,7 @@ fn test() {
 }
 
 mod method_calls {
-    struct StructForMethodCallTest {}
+    struct StructForMethodCallTest;
 
     impl StructForMethodCallTest {
         fn concrete_arg(&self, x: i32) {}
index 9ddd6d64701a61dd615ec0b1d1f5d5451601617f..7c7a9ecff67f5a56efb13c560ba21417a3cf32d9 100644 (file)
@@ -5,7 +5,7 @@
 use std::vec::Vec;
 
 #[derive(Copy, Clone)]
-struct SomeStruct {}
+struct SomeStruct;
 
 struct AnotherStruct {
     x: u8,
index 01de0be7caea96cb907223cb49ddce57233d6eef..88228afae89c005d698051baecdd76ee87f17ace 100644 (file)
@@ -5,7 +5,7 @@ LL |     drop(s1);
    |     ^^^^^^^^
    |
    = note: `-D clippy::drop-copy` implied by `-D warnings`
-note: argument has type SomeStruct
+note: argument has type `SomeStruct`
   --> $DIR/drop_forget_copy.rs:33:10
    |
 LL |     drop(s1);
@@ -17,7 +17,7 @@ error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a
 LL |     drop(s2);
    |     ^^^^^^^^
    |
-note: argument has type SomeStruct
+note: argument has type `SomeStruct`
   --> $DIR/drop_forget_copy.rs:34:10
    |
 LL |     drop(s2);
@@ -29,7 +29,7 @@ error: calls to `std::mem::drop` with a value that implements `Copy`. Dropping a
 LL |     drop(s4);
    |     ^^^^^^^^
    |
-note: argument has type SomeStruct
+note: argument has type `SomeStruct`
   --> $DIR/drop_forget_copy.rs:36:10
    |
 LL |     drop(s4);
@@ -42,7 +42,7 @@ LL |     forget(s1);
    |     ^^^^^^^^^^
    |
    = note: `-D clippy::forget-copy` implied by `-D warnings`
-note: argument has type SomeStruct
+note: argument has type `SomeStruct`
   --> $DIR/drop_forget_copy.rs:39:12
    |
 LL |     forget(s1);
@@ -54,7 +54,7 @@ error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetti
 LL |     forget(s2);
    |     ^^^^^^^^^^
    |
-note: argument has type SomeStruct
+note: argument has type `SomeStruct`
   --> $DIR/drop_forget_copy.rs:40:12
    |
 LL |     forget(s2);
@@ -66,7 +66,7 @@ error: calls to `std::mem::forget` with a value that implements `Copy`. Forgetti
 LL |     forget(s4);
    |     ^^^^^^^^^^
    |
-note: argument has type SomeStruct
+note: argument has type `SomeStruct`
   --> $DIR/drop_forget_copy.rs:42:12
    |
 LL |     forget(s4);
diff --git a/tests/ui/drop_non_drop.rs b/tests/ui/drop_non_drop.rs
new file mode 100644 (file)
index 0000000..5a0ebde
--- /dev/null
@@ -0,0 +1,40 @@
+#![warn(clippy::drop_non_drop)]
+
+use core::mem::drop;
+
+fn make_result<T>(t: T) -> Result<T, ()> {
+    Ok(t)
+}
+
+#[must_use]
+fn must_use<T>(t: T) -> T {
+    t
+}
+
+fn drop_generic<T>(t: T) {
+    // Don't lint
+    drop(t)
+}
+
+fn main() {
+    struct Foo;
+    // Lint
+    drop(Foo);
+    // Don't lint
+    drop(make_result(Foo));
+    // Don't lint
+    drop(must_use(Foo));
+
+    struct Bar;
+    impl Drop for Bar {
+        fn drop(&mut self) {}
+    }
+    // Don't lint
+    drop(Bar);
+
+    struct Baz<T>(T);
+    // Lint
+    drop(Baz(Foo));
+    // Don't lint
+    drop(Baz(Bar));
+}
diff --git a/tests/ui/drop_non_drop.stderr b/tests/ui/drop_non_drop.stderr
new file mode 100644 (file)
index 0000000..f730689
--- /dev/null
@@ -0,0 +1,27 @@
+error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes
+  --> $DIR/drop_non_drop.rs:22:5
+   |
+LL |     drop(Foo);
+   |     ^^^^^^^^^
+   |
+   = note: `-D clippy::drop-non-drop` implied by `-D warnings`
+note: argument has type `main::Foo`
+  --> $DIR/drop_non_drop.rs:22:10
+   |
+LL |     drop(Foo);
+   |          ^^^
+
+error: call to `std::mem::drop` with a value that does not implement `Drop`. Dropping such a type only extends it's contained lifetimes
+  --> $DIR/drop_non_drop.rs:37:5
+   |
+LL |     drop(Baz(Foo));
+   |     ^^^^^^^^^^^^^^
+   |
+note: argument has type `main::Baz<main::Foo>`
+  --> $DIR/drop_non_drop.rs:37:10
+   |
+LL |     drop(Baz(Foo));
+   |          ^^^^^^^^
+
+error: aborting due to 2 previous errors
+
index e1a15c609fd233d6adf784fb5294c7a3b191baed..7de0b0bbdf9ae8ffdfb4019d695e4a38df8ad197 100644 (file)
@@ -1,7 +1,7 @@
 #![warn(clippy::drop_ref)]
 #![allow(clippy::toplevel_ref_arg)]
 #![allow(clippy::map_err_ignore)]
-#![allow(clippy::unnecessary_wraps)]
+#![allow(clippy::unnecessary_wraps, clippy::drop_non_drop)]
 
 use std::mem::drop;
 
diff --git a/tests/ui/empty_structs_with_brackets.fixed b/tests/ui/empty_structs_with_brackets.fixed
new file mode 100644 (file)
index 0000000..80f0760
--- /dev/null
@@ -0,0 +1,25 @@
+// run-rustfix
+#![warn(clippy::empty_structs_with_brackets)]
+#![allow(dead_code)]
+
+pub struct MyEmptyStruct; // should trigger lint
+struct MyEmptyTupleStruct; // should trigger lint
+
+// should not trigger lint
+struct MyCfgStruct {
+    #[cfg(feature = "thisisneverenabled")]
+    field: u8,
+}
+
+// should not trigger lint
+struct MyCfgTupleStruct(#[cfg(feature = "thisisneverenabled")] u8);
+
+// should not trigger lint
+struct MyStruct {
+    field: u8,
+}
+struct MyTupleStruct(usize, String); // should not trigger lint
+struct MySingleTupleStruct(usize); // should not trigger lint
+struct MyUnitLikeStruct; // should not trigger lint
+
+fn main() {}
diff --git a/tests/ui/empty_structs_with_brackets.rs b/tests/ui/empty_structs_with_brackets.rs
new file mode 100644 (file)
index 0000000..1d1ed4c
--- /dev/null
@@ -0,0 +1,25 @@
+// run-rustfix
+#![warn(clippy::empty_structs_with_brackets)]
+#![allow(dead_code)]
+
+pub struct MyEmptyStruct {} // should trigger lint
+struct MyEmptyTupleStruct(); // should trigger lint
+
+// should not trigger lint
+struct MyCfgStruct {
+    #[cfg(feature = "thisisneverenabled")]
+    field: u8,
+}
+
+// should not trigger lint
+struct MyCfgTupleStruct(#[cfg(feature = "thisisneverenabled")] u8);
+
+// should not trigger lint
+struct MyStruct {
+    field: u8,
+}
+struct MyTupleStruct(usize, String); // should not trigger lint
+struct MySingleTupleStruct(usize); // should not trigger lint
+struct MyUnitLikeStruct; // should not trigger lint
+
+fn main() {}
diff --git a/tests/ui/empty_structs_with_brackets.stderr b/tests/ui/empty_structs_with_brackets.stderr
new file mode 100644 (file)
index 0000000..0308cb5
--- /dev/null
@@ -0,0 +1,19 @@
+error: found empty brackets on struct declaration
+  --> $DIR/empty_structs_with_brackets.rs:5:25
+   |
+LL | pub struct MyEmptyStruct {} // should trigger lint
+   |                         ^^^
+   |
+   = note: `-D clippy::empty-structs-with-brackets` implied by `-D warnings`
+   = help: remove the brackets
+
+error: found empty brackets on struct declaration
+  --> $DIR/empty_structs_with_brackets.rs:6:26
+   |
+LL | struct MyEmptyTupleStruct(); // should trigger lint
+   |                          ^^^
+   |
+   = help: remove the brackets
+
+error: aborting due to 2 previous errors
+
diff --git a/tests/ui/err_expect.fixed b/tests/ui/err_expect.fixed
new file mode 100644 (file)
index 0000000..7e18d70
--- /dev/null
@@ -0,0 +1,14 @@
+// run-rustfix
+
+struct MyTypeNonDebug;
+
+#[derive(Debug)]
+struct MyTypeDebug;
+
+fn main() {
+    let test_debug: Result<MyTypeDebug, u32> = Ok(MyTypeDebug);
+    test_debug.expect_err("Testing debug type");
+
+    let test_non_debug: Result<MyTypeNonDebug, u32> = Ok(MyTypeNonDebug);
+    test_non_debug.err().expect("Testing non debug type");
+}
diff --git a/tests/ui/err_expect.rs b/tests/ui/err_expect.rs
new file mode 100644 (file)
index 0000000..bf8c3c9
--- /dev/null
@@ -0,0 +1,14 @@
+// run-rustfix
+
+struct MyTypeNonDebug;
+
+#[derive(Debug)]
+struct MyTypeDebug;
+
+fn main() {
+    let test_debug: Result<MyTypeDebug, u32> = Ok(MyTypeDebug);
+    test_debug.err().expect("Testing debug type");
+
+    let test_non_debug: Result<MyTypeNonDebug, u32> = Ok(MyTypeNonDebug);
+    test_non_debug.err().expect("Testing non debug type");
+}
diff --git a/tests/ui/err_expect.stderr b/tests/ui/err_expect.stderr
new file mode 100644 (file)
index 0000000..ffd97e0
--- /dev/null
@@ -0,0 +1,10 @@
+error: called `.err().expect()` on a `Result` value
+  --> $DIR/err_expect.rs:10:16
+   |
+LL |     test_debug.err().expect("Testing debug type");
+   |                ^^^^^^^^^^^^ help: try: `expect_err`
+   |
+   = note: `-D clippy::err-expect` implied by `-D warnings`
+
+error: aborting due to previous error
+
index 1442ee08e7546aa20056ccb5b3f27e894b6c4cb8..f805bcc9ba8af503140bf2cff10433824147971b 100644 (file)
@@ -20,7 +20,7 @@ fn h(_: bool, _: bool, _: bool) {}
 fn e(_: S, _: S, _: Box<S>, _: Vec<u32>) {}
 fn t(_: S, _: S, _: Box<S>, _: Vec<u32>, _: bool, _: bool, _: bool, _: bool) {}
 
-struct S {}
+struct S;
 trait Trait {
     fn f(_: bool, _: bool, _: bool, _: bool);
     fn g(_: bool, _: bool, _: bool, _: Vec<u32>);
diff --git a/tests/ui/forget_non_drop.rs b/tests/ui/forget_non_drop.rs
new file mode 100644 (file)
index 0000000..7580cf9
--- /dev/null
@@ -0,0 +1,27 @@
+#![warn(clippy::forget_non_drop)]
+
+use core::mem::forget;
+
+fn forget_generic<T>(t: T) {
+    // Don't lint
+    forget(t)
+}
+
+fn main() {
+    struct Foo;
+    // Lint
+    forget(Foo);
+
+    struct Bar;
+    impl Drop for Bar {
+        fn drop(&mut self) {}
+    }
+    // Don't lint
+    forget(Bar);
+
+    struct Baz<T>(T);
+    // Lint
+    forget(Baz(Foo));
+    // Don't lint
+    forget(Baz(Bar));
+}
diff --git a/tests/ui/forget_non_drop.stderr b/tests/ui/forget_non_drop.stderr
new file mode 100644 (file)
index 0000000..03fb009
--- /dev/null
@@ -0,0 +1,27 @@
+error: call to `std::mem::forget` with a value that does not implement `Drop`. Forgetting such a type is the same as dropping it
+  --> $DIR/forget_non_drop.rs:13:5
+   |
+LL |     forget(Foo);
+   |     ^^^^^^^^^^^
+   |
+   = note: `-D clippy::forget-non-drop` implied by `-D warnings`
+note: argument has type `main::Foo`
+  --> $DIR/forget_non_drop.rs:13:12
+   |
+LL |     forget(Foo);
+   |            ^^^
+
+error: call to `std::mem::forget` with a value that does not implement `Drop`. Forgetting such a type is the same as dropping it
+  --> $DIR/forget_non_drop.rs:24:5
+   |
+LL |     forget(Baz(Foo));
+   |     ^^^^^^^^^^^^^^^^
+   |
+note: argument has type `main::Baz<main::Foo>`
+  --> $DIR/forget_non_drop.rs:24:12
+   |
+LL |     forget(Baz(Foo));
+   |            ^^^^^^^^
+
+error: aborting due to 2 previous errors
+
index c49e6756a6c5bed9db7e73e40ae7acbf5aebb260..6c8c4c9c0edecc01adeaee75a87681af5b94dc06 100644 (file)
@@ -1,6 +1,6 @@
 #![warn(clippy::forget_ref)]
 #![allow(clippy::toplevel_ref_arg)]
-#![allow(clippy::unnecessary_wraps)]
+#![allow(clippy::unnecessary_wraps, clippy::forget_non_drop)]
 
 use std::mem::forget;
 
index 12bbda71f434813ca78c1e499bb3d70e82106490..edc3fe1aec13a556fa0c8c2121a1639553c57952 100644 (file)
@@ -66,4 +66,13 @@ fn main() {
     let b = a << 0; // no error: non-integer
 
     1 * Meter; // no error: non-integer
+
+    2 % 3;
+    -2 % 3;
+    2 % -3 + x;
+    -2 % -3 + x;
+    x + 1 % 3;
+    (x + 1) % 3; // no error
+    4 % 3; // no error
+    4 % -3; // no error
 }
index 0103cf5457e81a8a313dc1b78506497679903647..706f01a3dd6c403b245ca8568cdb624def8f943a 100644 (file)
@@ -78,5 +78,35 @@ error: the operation is ineffective. Consider reducing it to `x`
 LL |     x >> &0;
    |     ^^^^^^^
 
-error: aborting due to 13 previous errors
+error: the operation is ineffective. Consider reducing it to `2`
+  --> $DIR/identity_op.rs:70:5
+   |
+LL |     2 % 3;
+   |     ^^^^^
+
+error: the operation is ineffective. Consider reducing it to `-2`
+  --> $DIR/identity_op.rs:71:5
+   |
+LL |     -2 % 3;
+   |     ^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `2`
+  --> $DIR/identity_op.rs:72:5
+   |
+LL |     2 % -3 + x;
+   |     ^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `-2`
+  --> $DIR/identity_op.rs:73:5
+   |
+LL |     -2 % -3 + x;
+   |     ^^^^^^^
+
+error: the operation is ineffective. Consider reducing it to `1`
+  --> $DIR/identity_op.rs:74:9
+   |
+LL |     x + 1 % 3;
+   |         ^^^^^
+
+error: aborting due to 18 previous errors
 
index 639fecb8927bd322b4754c9fb00268107612e793..2549c9f32f9049326e8ae416a651a81526a23b47 100644 (file)
@@ -30,7 +30,7 @@ pub fn own_different<T, U>(v: T) -> U
 }
 
 #[derive(Copy, Clone)]
-struct Kitten {}
+struct Kitten;
 impl Kitten {
     // badly named method
     fn to_vec(self) -> Kitten {
@@ -44,7 +44,7 @@ fn borrow(&self) -> &BorrowedKitten {
     }
 }
 
-struct BorrowedKitten {}
+struct BorrowedKitten;
 impl ToOwned for BorrowedKitten {
     type Owned = Kitten;
     fn to_owned(&self) -> Kitten {
index ca8ca53c80c3f436956fef359df23a0778ebd111..45a430edcb58998562e96b35e86d8c2a41db1e8b 100644 (file)
@@ -1,18 +1,34 @@
+#![feature(inline_const)]
 #![warn(clippy::indexing_slicing)]
 // We also check the out_of_bounds_indexing lint here, because it lints similar things and
 // we want to avoid false positives.
 #![warn(clippy::out_of_bounds_indexing)]
-#![allow(clippy::no_effect, clippy::unnecessary_operation)]
+#![allow(const_err, clippy::no_effect, clippy::unnecessary_operation)]
+
+const ARR: [i32; 2] = [1, 2];
+const REF: &i32 = &ARR[idx()]; // Ok, should not produce stderr.
+const REF_ERR: &i32 = &ARR[idx4()]; // Ok, let rustc handle const contexts.
+
+const fn idx() -> usize {
+    1
+}
+const fn idx4() -> usize {
+    4
+}
 
 fn main() {
     let x = [1, 2, 3, 4];
     let index: usize = 1;
     x[index];
-    x[4]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
-    x[1 << 3]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
+    x[4]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
+    x[1 << 3]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
 
     x[0]; // Ok, should not produce stderr.
     x[3]; // Ok, should not produce stderr.
+    x[const { idx() }]; // Ok, should not produce stderr.
+    x[const { idx4() }]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
+    const { &ARR[idx()] }; // Ok, should not produce stderr.
+    const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
 
     let y = &x;
     y[0]; // Ok, referencing shouldn't affect this lint. See the issue 6021
@@ -25,7 +41,7 @@ fn main() {
 
     const N: usize = 15; // Out of bounds
     const M: usize = 3; // In bounds
-    x[N]; // Ok, let rustc's `const_err` lint handle `usize` indexing on arrays.
+    x[N]; // Ok, let rustc's `unconditional_panic` lint handle `usize` indexing on arrays.
     x[M]; // Ok, should not produce stderr.
     v[N];
     v[M];
index 76ecec3348400f98e02de0356d316a619061c261..83a36f407d5d877b18511182ca893781bbc628aa 100644 (file)
@@ -1,5 +1,17 @@
+error[E0080]: evaluation of `main::{constant#3}::<&i32>` failed
+  --> $DIR/indexing_slicing_index.rs:31:14
+   |
+LL |     const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
+   |              ^^^^^^^^^^^ index out of bounds: the length is 2 but the index is 4
+
+error[E0080]: erroneous constant used
+  --> $DIR/indexing_slicing_index.rs:31:5
+   |
+LL |     const { &ARR[idx4()] }; // Ok, let rustc handle const contexts.
+   |     ^^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+
 error: indexing may panic
-  --> $DIR/indexing_slicing_index.rs:10:5
+  --> $DIR/indexing_slicing_index.rs:22:5
    |
 LL |     x[index];
    |     ^^^^^^^^
@@ -8,7 +20,7 @@ LL |     x[index];
    = help: consider using `.get(n)` or `.get_mut(n)` instead
 
 error: indexing may panic
-  --> $DIR/indexing_slicing_index.rs:22:5
+  --> $DIR/indexing_slicing_index.rs:38:5
    |
 LL |     v[0];
    |     ^^^^
@@ -16,7 +28,7 @@ LL |     v[0];
    = help: consider using `.get(n)` or `.get_mut(n)` instead
 
 error: indexing may panic
-  --> $DIR/indexing_slicing_index.rs:23:5
+  --> $DIR/indexing_slicing_index.rs:39:5
    |
 LL |     v[10];
    |     ^^^^^
@@ -24,7 +36,7 @@ LL |     v[10];
    = help: consider using `.get(n)` or `.get_mut(n)` instead
 
 error: indexing may panic
-  --> $DIR/indexing_slicing_index.rs:24:5
+  --> $DIR/indexing_slicing_index.rs:40:5
    |
 LL |     v[1 << 3];
    |     ^^^^^^^^^
@@ -32,7 +44,7 @@ LL |     v[1 << 3];
    = help: consider using `.get(n)` or `.get_mut(n)` instead
 
 error: indexing may panic
-  --> $DIR/indexing_slicing_index.rs:30:5
+  --> $DIR/indexing_slicing_index.rs:46:5
    |
 LL |     v[N];
    |     ^^^^
@@ -40,12 +52,13 @@ LL |     v[N];
    = help: consider using `.get(n)` or `.get_mut(n)` instead
 
 error: indexing may panic
-  --> $DIR/indexing_slicing_index.rs:31:5
+  --> $DIR/indexing_slicing_index.rs:47:5
    |
 LL |     v[M];
    |     ^^^^
    |
    = help: consider using `.get(n)` or `.get_mut(n)` instead
 
-error: aborting due to 6 previous errors
+error: aborting due to 8 previous errors
 
+For more information about this error, try `rustc --explain E0080`.
index b54147c94d192bea1c3ec58030aebd824b4f9bf2..f23671c26e4cc55b43615564d8fd28148b3a6a98 100644 (file)
@@ -3,7 +3,7 @@
 #![warn(clippy::iter_nth_zero)]
 use std::collections::HashSet;
 
-struct Foo {}
+struct Foo;
 
 impl Foo {
     fn nth(&self, index: usize) -> usize {
index b92c7d18adb4fb658484c29a640999e23907946b..7c968d49845714574db74baaf2178eff36244467 100644 (file)
@@ -3,7 +3,7 @@
 #![warn(clippy::iter_nth_zero)]
 use std::collections::HashSet;
 
-struct Foo {}
+struct Foo;
 
 impl Foo {
     fn nth(&self, index: usize) -> usize {
index a9041671101b5006f437f516f3280545a6f67413..56761ebbcb80bb0e5dcd595ba3ca0194b41829e2 100644 (file)
@@ -1,5 +1,6 @@
 // run-rustfix
 #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
+#![allow(dead_code)]
 
 fn main() {
     let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()];
@@ -43,3 +44,8 @@ fn main() {
     // Should probably stay as it is.
     let _ = [0, 1, 2, 3, 4].iter().cloned().take(10);
 }
+
+// #8527
+fn cloned_flatten(x: Option<&Option<String>>) -> Option<String> {
+    x.cloned().flatten()
+}
index dd04e33a4b3aeedb8901b55a5d7e49f4d611852f..98321d889b58273fd78a023d8933ad3af3f53074 100644 (file)
@@ -1,5 +1,6 @@
 // run-rustfix
 #![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
+#![allow(dead_code)]
 
 fn main() {
     let vec = vec!["1".to_string(), "2".to_string(), "3".to_string()];
@@ -45,3 +46,8 @@ fn main() {
     // Should probably stay as it is.
     let _ = [0, 1, 2, 3, 4].iter().cloned().take(10);
 }
+
+// #8527
+fn cloned_flatten(x: Option<&Option<String>>) -> Option<String> {
+    x.cloned().flatten()
+}
index e36b0e36fbdf98c6f4346682bd3a44df84ffac3a..0582700fd16a8bde34e9b13c93786ad9e467c8e6 100644 (file)
@@ -1,5 +1,5 @@
 error: called `cloned().last()` on an `Iterator`. It may be more efficient to call `last().cloned()` instead
-  --> $DIR/iter_overeager_cloned.rs:7:29
+  --> $DIR/iter_overeager_cloned.rs:8:29
    |
 LL |     let _: Option<String> = vec.iter().cloned().last();
    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().last().cloned()`
@@ -7,13 +7,13 @@ LL |     let _: Option<String> = vec.iter().cloned().last();
    = note: `-D clippy::iter-overeager-cloned` implied by `-D warnings`
 
 error: called `cloned().next()` on an `Iterator`. It may be more efficient to call `next().cloned()` instead
-  --> $DIR/iter_overeager_cloned.rs:9:29
+  --> $DIR/iter_overeager_cloned.rs:10:29
    |
 LL |     let _: Option<String> = vec.iter().chain(vec.iter()).cloned().next();
    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().chain(vec.iter()).next().cloned()`
 
 error: called `cloned().count()` on an `Iterator`. It may be more efficient to call `count()` instead
-  --> $DIR/iter_overeager_cloned.rs:11:20
+  --> $DIR/iter_overeager_cloned.rs:12:20
    |
 LL |     let _: usize = vec.iter().filter(|x| x == &"2").cloned().count();
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().filter(|x| x == &"2").count()`
@@ -21,25 +21,25 @@ LL |     let _: usize = vec.iter().filter(|x| x == &"2").cloned().count();
    = note: `-D clippy::redundant-clone` implied by `-D warnings`
 
 error: called `cloned().take(...)` on an `Iterator`. It may be more efficient to call `take(...).cloned()` instead
-  --> $DIR/iter_overeager_cloned.rs:13:21
+  --> $DIR/iter_overeager_cloned.rs:14:21
    |
 LL |     let _: Vec<_> = vec.iter().cloned().take(2).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().take(2).cloned()`
 
 error: called `cloned().skip(...)` on an `Iterator`. It may be more efficient to call `skip(...).cloned()` instead
-  --> $DIR/iter_overeager_cloned.rs:15:21
+  --> $DIR/iter_overeager_cloned.rs:16:21
    |
 LL |     let _: Vec<_> = vec.iter().cloned().skip(2).collect();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().skip(2).cloned()`
 
 error: called `cloned().nth(...)` on an `Iterator`. It may be more efficient to call `nth(...).cloned()` instead
-  --> $DIR/iter_overeager_cloned.rs:17:13
+  --> $DIR/iter_overeager_cloned.rs:18:13
    |
 LL |     let _ = vec.iter().filter(|x| x == &"2").cloned().nth(2);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `vec.iter().filter(|x| x == &"2").nth(2).cloned()`
 
 error: called `cloned().flatten()` on an `Iterator`. It may be more efficient to call `flatten().cloned()` instead
-  --> $DIR/iter_overeager_cloned.rs:19:13
+  --> $DIR/iter_overeager_cloned.rs:20:13
    |
 LL |       let _ = [Some(Some("str".to_string())), Some(Some("str".to_string()))]
    |  _____________^
index e4a2e9df4d7ba71d6ad41d0f9faa75d3f7ced853..7601b5c66fa35057f69ea8cd989b5df689761e3c 100644 (file)
@@ -37,7 +37,7 @@ pub trait PubLargeTypeDevourer {
     fn devoure_array_in_public(&self, array: [u8; 6666]);
 }
 
-struct S {}
+struct S;
 impl LargeTypeDevourer for S {
     fn devoure_array(&self, array: [u8; 6666]) {
         todo!();
index e3561863c1e1ff7f00f1e52703e808cdb1c64e63..bb162adc9adb26309f3b0968936a4e378ec0cfe4 100644 (file)
@@ -88,7 +88,7 @@ fn test(value: Weak<RefCell<Bar>>) -> u32 {
             ret
         }
 
-        struct Bar {}
+        struct Bar;
 
         impl Bar {
             fn new() -> Self {
index a842e872a37b1f0fefcb21dad35b630ef75deaeb..1edb77c748bfb715c0a3ddbcf2db2b19237238c7 100644 (file)
@@ -26,7 +26,7 @@ fn h() -> u32 {
     0
 }
 
-struct S {}
+struct S;
 
 impl S {
     #[must_use]
index 136cc96be70cafa57951fc7edd89c784892803b1..b7e46a4a8ccc284cb9b8a5640e00bc944a762314 100644 (file)
@@ -38,7 +38,7 @@ async fn already_async() -> impl Future<Output = i32> {
     async { 42 }
 }
 
-struct S {}
+struct S;
 impl S {
     async fn inh_fut() -> i32 {
         // NOTE: this code is here just to check that the indentation is correct in the suggested fix
index ddc453ffdb7500958c5e26cbf62a65a4859640f7..b05429da6622500a9b35a606ed4967317419cb85 100644 (file)
@@ -52,7 +52,7 @@ async fn already_async() -> impl Future<Output = i32> {
     async { 42 }
 }
 
-struct S {}
+struct S;
 impl S {
     fn inh_fut() -> impl Future<Output = i32> {
         async {
index 05d6c56f2aca0a5dd63a3d635468800d0431a5e3..7d68978216c9c67b8931a9562e96d3e7791930de 100644 (file)
@@ -78,7 +78,7 @@ fn result_unwrap_or() {
     (Ok(1) as Result<i32, &str>).unwrap_or(42);
 
     // method call case, suggestion must not surround Result expr `s.method()` with parentheses
-    struct S {}
+    struct S;
     impl S {
         fn method(self) -> Option<i32> {
             Some(42)
index 09f62c69b71de15b832c836a03c21829a78d501f..b937fe6f977e5b7393d3423b9573ab5b903f89ea 100644 (file)
@@ -102,7 +102,7 @@ fn result_unwrap_or() {
     };
 
     // method call case, suggestion must not surround Result expr `s.method()` with parentheses
-    struct S {}
+    struct S;
     impl S {
         fn method(self) -> Option<i32> {
             Some(42)
index 4a1452b25f343923c71059722e6d0fe3d32dcc6f..2256e51f2d09c49550f3f6ccd7c74034c1b82ab8 100644 (file)
@@ -16,6 +16,8 @@ fn main() {
     let _: Result<i8, f32> = Err(2.3).map(|x: i8| {
         return x + 3;
     });
+    let _: Result<u32, u32> = Ok(1);
+    let _: Result<u32, u32> = Ok(1).map_err(|a: u32| a * 42);
 }
 
 fn not_identity(x: &u16) -> u16 {
index 65c7e6e1ea554a7db9d691ce237337a450e2c3ed..ccfdc9ea76d522ce6d888d04aafdbd401fda0959 100644 (file)
@@ -18,6 +18,8 @@ fn main() {
     let _: Result<i8, f32> = Err(2.3).map(|x: i8| {
         return x + 3;
     });
+    let _: Result<u32, u32> = Ok(1).map_err(|a| a);
+    let _: Result<u32, u32> = Ok(1).map_err(|a: u32| a * 42);
 }
 
 fn not_identity(x: &u16) -> u16 {
index e4a0320cbda55721fffd017087d3db81a0868937..b6a77281f6de2e6424c76bd23f1dab64e99469a7 100644 (file)
@@ -33,5 +33,11 @@ LL | |         return x;
 LL | |     });
    | |______^ help: remove the call to `map`
 
-error: aborting due to 5 previous errors
+error: unnecessary map of the identity function
+  --> $DIR/map_identity.rs:21:36
+   |
+LL |     let _: Result<u32, u32> = Ok(1).map_err(|a| a);
+   |                                    ^^^^^^^^^^^^^^^ help: remove the call to `map_err`
+
+error: aborting due to 6 previous errors
 
index 9a74da4e3b8b641ef4387246ab1f24ea7800de4d..e7f07b50f3ab1fbad149d309b3da9c67bd007950 100644 (file)
@@ -1,5 +1,5 @@
 #![allow(unused)]
-struct Mappable {}
+struct Mappable;
 
 impl Mappable {
     pub fn map(&self) {}
index c5f221220ece7374caeab1c21dc14c35e6b8751e..f83c3e0e281ca29ccad4714e491feb26a6b18f5e 100644 (file)
@@ -99,7 +99,7 @@ pub fn manual_range_contains() {
 }
 
 pub fn use_self() {
-    struct Foo {}
+    struct Foo;
 
     impl Foo {
         fn new() -> Foo {
@@ -145,6 +145,16 @@ fn int_from_bool() -> u8 {
     true as u8
 }
 
+fn err_expect() {
+    let x: Result<u32, &str> = Ok(10);
+    x.err().expect("Testing expect_err");
+}
+
+fn cast_abs_to_unsigned() {
+    let x: i32 = 10;
+    assert_eq!(10u32, x.abs() as u32);
+}
+
 fn main() {
     filter_map_next();
     checked_conversion();
@@ -162,6 +172,8 @@ fn main() {
     missing_const_for_fn();
     unnest_or_patterns();
     int_from_bool();
+    err_expect();
+    cast_abs_to_unsigned();
 }
 
 mod just_under_msrv {
index 6b3fdb0844b49e1a0e2dd23dfc6c0fc70dcc4942..de225eb740d03dc9024df7276c6e72d9d399e760 100644 (file)
@@ -1,12 +1,12 @@
 error: stripping a prefix manually
-  --> $DIR/min_rust_version_attr.rs:186:24
+  --> $DIR/min_rust_version_attr.rs:198:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::manual-strip` implied by `-D warnings`
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:185:9
+  --> $DIR/min_rust_version_attr.rs:197:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -17,13 +17,13 @@ LL ~             assert_eq!(<stripped>.to_uppercase(), "WORLD!");
    |
 
 error: stripping a prefix manually
-  --> $DIR/min_rust_version_attr.rs:198:24
+  --> $DIR/min_rust_version_attr.rs:210:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:197:9
+  --> $DIR/min_rust_version_attr.rs:209:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
index b73b24b8e0a3b9b50fc6eaaf7b3e68d95168d324..07f8e3888c998274b8fa240a6fac1e72fd7f2e10 100644 (file)
@@ -7,8 +7,8 @@
 type Typedef = String;
 pub type PubTypedef = String;
 
-struct Foo {} // ok
-pub struct PubFoo {} // ok
+struct Foo; // ok
+pub struct PubFoo; // ok
 enum FooE {} // ok
 pub enum PubFooE {} // ok
 
@@ -63,4 +63,4 @@ pub fn PubFooImpl() {} // missing #[inline]
 
 // do not lint this since users cannot control the external code
 #[derive(Debug)]
-pub struct S {}
+pub struct S;
index f5908cb5701fbe9a3e62a1779951aad18baf6af6..ebaa77cc283e0420cd0c7f17085cf998524e6b33 100644 (file)
@@ -7,7 +7,7 @@ mod foo {
     pub fn foo() {}
     pub fn foo_bar() {}
     pub fn bar_foo() {}
-    pub struct FooCake {}
+    pub struct FooCake;
     pub enum CakeFoo {}
     pub struct Foo7Bar;
 
index bdd217a969c05acfcee5efe23b2e111b72451b62..3f343a3e430185f8ca8535370c363b1f2c308ace 100644 (file)
@@ -15,8 +15,8 @@ LL |     pub fn bar_foo() {}
 error: item name starts with its containing module's name
   --> $DIR/module_name_repetitions.rs:10:5
    |
-LL |     pub struct FooCake {}
-   |     ^^^^^^^^^^^^^^^^^^^^^
+LL |     pub struct FooCake;
+   |     ^^^^^^^^^^^^^^^^^^^
 
 error: item name ends with its containing module's name
   --> $DIR/module_name_repetitions.rs:11:5
index 047a29fa1e327201d12847ff71452f51cea4ec9d..3ebe46bc5be7c5b8b4108f4477b70663a5734b38 100644 (file)
@@ -1,5 +1,10 @@
 #![warn(clippy::modulo_arithmetic)]
-#![allow(clippy::no_effect, clippy::unnecessary_operation, clippy::modulo_one)]
+#![allow(
+    clippy::no_effect,
+    clippy::unnecessary_operation,
+    clippy::modulo_one,
+    clippy::identity_op
+)]
 
 fn main() {
     // Lint when both sides are const and of the opposite sign
index 64335f35f0f82d997a2b4e1e14f52fa9af68df28..11b5f77461ba2e93b94779543f77b8770ffbf440 100644 (file)
@@ -1,5 +1,5 @@
 error: you are using modulo operator on constants with different signs: `-1 % 2`
-  --> $DIR/modulo_arithmetic_integral_const.rs:6:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:11:5
    |
 LL |     -1 % 2;
    |     ^^^^^^
@@ -9,7 +9,7 @@ LL |     -1 % 2;
    = note: or consider using `rem_euclid` or similar function
 
 error: you are using modulo operator on constants with different signs: `1 % -2`
-  --> $DIR/modulo_arithmetic_integral_const.rs:7:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:12:5
    |
 LL |     1 % -2;
    |     ^^^^^^
@@ -18,7 +18,7 @@ LL |     1 % -2;
    = note: or consider using `rem_euclid` or similar function
 
 error: you are using modulo operator on constants with different signs: `-1 % 3`
-  --> $DIR/modulo_arithmetic_integral_const.rs:8:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:13:5
    |
 LL |     (1 - 2) % (1 + 2);
    |     ^^^^^^^^^^^^^^^^^
@@ -27,7 +27,7 @@ LL |     (1 - 2) % (1 + 2);
    = note: or consider using `rem_euclid` or similar function
 
 error: you are using modulo operator on constants with different signs: `3 % -1`
-  --> $DIR/modulo_arithmetic_integral_const.rs:9:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:14:5
    |
 LL |     (1 + 2) % (1 - 2);
    |     ^^^^^^^^^^^^^^^^^
@@ -36,7 +36,7 @@ LL |     (1 + 2) % (1 - 2);
    = note: or consider using `rem_euclid` or similar function
 
 error: you are using modulo operator on constants with different signs: `-35 % 300000`
-  --> $DIR/modulo_arithmetic_integral_const.rs:10:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:15:5
    |
 LL |     35 * (7 - 4 * 2) % (-500 * -600);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -45,7 +45,7 @@ LL |     35 * (7 - 4 * 2) % (-500 * -600);
    = note: or consider using `rem_euclid` or similar function
 
 error: you are using modulo operator on constants with different signs: `-1 % 2`
-  --> $DIR/modulo_arithmetic_integral_const.rs:12:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:17:5
    |
 LL |     -1i8 % 2i8;
    |     ^^^^^^^^^^
@@ -54,7 +54,7 @@ LL |     -1i8 % 2i8;
    = note: or consider using `rem_euclid` or similar function
 
 error: you are using modulo operator on constants with different signs: `1 % -2`
-  --> $DIR/modulo_arithmetic_integral_const.rs:13:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:18:5
    |
 LL |     1i8 % -2i8;
    |     ^^^^^^^^^^
@@ -63,7 +63,7 @@ LL |     1i8 % -2i8;
    = note: or consider using `rem_euclid` or similar function
 
 error: you are using modulo operator on constants with different signs: `-1 % 2`
-  --> $DIR/modulo_arithmetic_integral_const.rs:14:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:19:5
    |
 LL |     -1i16 % 2i16;
    |     ^^^^^^^^^^^^
@@ -72,7 +72,7 @@ LL |     -1i16 % 2i16;
    = note: or consider using `rem_euclid` or similar function
 
 error: you are using modulo operator on constants with different signs: `1 % -2`
-  --> $DIR/modulo_arithmetic_integral_const.rs:15:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:20:5
    |
 LL |     1i16 % -2i16;
    |     ^^^^^^^^^^^^
@@ -81,7 +81,7 @@ LL |     1i16 % -2i16;
    = note: or consider using `rem_euclid` or similar function
 
 error: you are using modulo operator on constants with different signs: `-1 % 2`
-  --> $DIR/modulo_arithmetic_integral_const.rs:16:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:21:5
    |
 LL |     -1i32 % 2i32;
    |     ^^^^^^^^^^^^
@@ -90,7 +90,7 @@ LL |     -1i32 % 2i32;
    = note: or consider using `rem_euclid` or similar function
 
 error: you are using modulo operator on constants with different signs: `1 % -2`
-  --> $DIR/modulo_arithmetic_integral_const.rs:17:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:22:5
    |
 LL |     1i32 % -2i32;
    |     ^^^^^^^^^^^^
@@ -99,7 +99,7 @@ LL |     1i32 % -2i32;
    = note: or consider using `rem_euclid` or similar function
 
 error: you are using modulo operator on constants with different signs: `-1 % 2`
-  --> $DIR/modulo_arithmetic_integral_const.rs:18:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:23:5
    |
 LL |     -1i64 % 2i64;
    |     ^^^^^^^^^^^^
@@ -108,7 +108,7 @@ LL |     -1i64 % 2i64;
    = note: or consider using `rem_euclid` or similar function
 
 error: you are using modulo operator on constants with different signs: `1 % -2`
-  --> $DIR/modulo_arithmetic_integral_const.rs:19:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:24:5
    |
 LL |     1i64 % -2i64;
    |     ^^^^^^^^^^^^
@@ -117,7 +117,7 @@ LL |     1i64 % -2i64;
    = note: or consider using `rem_euclid` or similar function
 
 error: you are using modulo operator on constants with different signs: `-1 % 2`
-  --> $DIR/modulo_arithmetic_integral_const.rs:20:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:25:5
    |
 LL |     -1i128 % 2i128;
    |     ^^^^^^^^^^^^^^
@@ -126,7 +126,7 @@ LL |     -1i128 % 2i128;
    = note: or consider using `rem_euclid` or similar function
 
 error: you are using modulo operator on constants with different signs: `1 % -2`
-  --> $DIR/modulo_arithmetic_integral_const.rs:21:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:26:5
    |
 LL |     1i128 % -2i128;
    |     ^^^^^^^^^^^^^^
@@ -135,7 +135,7 @@ LL |     1i128 % -2i128;
    = note: or consider using `rem_euclid` or similar function
 
 error: you are using modulo operator on constants with different signs: `-1 % 2`
-  --> $DIR/modulo_arithmetic_integral_const.rs:22:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:27:5
    |
 LL |     -1isize % 2isize;
    |     ^^^^^^^^^^^^^^^^
@@ -144,7 +144,7 @@ LL |     -1isize % 2isize;
    = note: or consider using `rem_euclid` or similar function
 
 error: you are using modulo operator on constants with different signs: `1 % -2`
-  --> $DIR/modulo_arithmetic_integral_const.rs:23:5
+  --> $DIR/modulo_arithmetic_integral_const.rs:28:5
    |
 LL |     1isize % -2isize;
    |     ^^^^^^^^^^^^^^^^
index ad0d694a2174a3a125393790d5767a81afd9c98d..02b43cce2bd4c5b77a46ee1d73088f83ad8732ca 100644 (file)
@@ -14,7 +14,7 @@ trait T1 {
                 fn test(self: &Self);
             }
 
-            struct S1 {}
+            struct S1;
 
             impl T1 for S1 {
                 fn test(self: &Self) {}
@@ -32,7 +32,7 @@ trait T2 {
         fn call_with_mut_self(&mut self);
     }
 
-    struct S2 {}
+    struct S2;
 
     // The method's signature will be expanded to:
     //  fn call_with_mut_self<'life0>(self: &'life0 mut Self) {}
index f3eafe8e9279e2167bb2d7dfe7f94dd5deb3b15a..1456204ca8692fdd79f65e4540946674c2d9f7e2 100644 (file)
@@ -268,7 +268,7 @@ fn needless_lt<'a>(_x: &'a u8) {}
 
 mod issue2944 {
     trait Foo {}
-    struct Bar {}
+    struct Bar;
     struct Baz<'a> {
         bar: &'a Bar,
     }
index ece18ad737fdad5264a22085a2dcfb94003a83b8..9ccccaa1725a65dc8867a1a48e9698c811682886 100644 (file)
@@ -4,38 +4,35 @@
 #![allow(dead_code)]
 
 #[derive(Clone, Copy)]
-enum Choice {
+enum Simple {
     A,
     B,
     C,
     D,
 }
 
-#[allow(unused_mut)]
 fn useless_match() {
-    let mut i = 10;
+    let i = 10;
     let _: i32 = i;
-    let _: i32 = i;
-    let mut _i_mut = i;
-
     let s = "test";
     let _: &str = s;
 }
 
-fn custom_type_match(se: Choice) {
-    let _: Choice = se;
+fn custom_type_match() {
+    let se = Simple::A;
+    let _: Simple = se;
     // Don't trigger
-    let _: Choice = match se {
-        Choice::A => Choice::A,
-        Choice::B => Choice::B,
-        _ => Choice::C,
+    let _: Simple = match se {
+        Simple::A => Simple::A,
+        Simple::B => Simple::B,
+        _ => Simple::C,
     };
     // Mingled, don't trigger
-    let _: Choice = match se {
-        Choice::A => Choice::B,
-        Choice::B => Choice::C,
-        Choice::C => Choice::D,
-        Choice::D => Choice::A,
+    let _: Simple = match se {
+        Simple::A => Simple::B,
+        Simple::B => Simple::C,
+        Simple::C => Simple::D,
+        Simple::D => Simple::A,
     };
 }
 
@@ -55,29 +52,146 @@ fn func_ret_err<T>(err: T) -> Result<i32, T> {
 fn result_match() {
     let _: Result<i32, i32> = Ok(1);
     let _: Result<i32, i32> = func_ret_err(0_i32);
+    // as ref, don't trigger
+    let res = &func_ret_err(0_i32);
+    let _: Result<&i32, &i32> = match *res {
+        Ok(ref x) => Ok(x),
+        Err(ref x) => Err(x),
+    };
 }
 
-fn if_let_option() -> Option<i32> {
-    Some(1)
+fn if_let_option() {
+    let _ = Some(1);
+
+    fn do_something() {}
+
+    // Don't trigger
+    let _ = if let Some(a) = Some(1) {
+        Some(a)
+    } else {
+        do_something();
+        None
+    };
+
+    // Don't trigger
+    let _ = if let Some(a) = Some(1) {
+        do_something();
+        Some(a)
+    } else {
+        None
+    };
 }
 
-fn if_let_result(x: Result<(), i32>) {
-    let _: Result<(), i32> = x;
-    let _: Result<(), i32> = x;
+fn if_let_result() {
+    let x: Result<i32, i32> = Ok(1);
+    let _: Result<i32, i32> = x;
+    let _: Result<i32, i32> = x;
     // Input type mismatch, don't trigger
-    let _: Result<(), i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
+    let _: Result<i32, i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
 }
 
-fn if_let_custom_enum(x: Choice) {
-    let _: Choice = x;
+fn if_let_custom_enum(x: Simple) {
+    let _: Simple = x;
+
     // Don't trigger
-    let _: Choice = if let Choice::A = x {
-        Choice::A
+    let _: Simple = if let Simple::A = x {
+        Simple::A
     } else if true {
-        Choice::B
+        Simple::B
     } else {
         x
     };
 }
 
+mod issue8542 {
+    #[derive(Clone, Copy)]
+    enum E {
+        VariantA(u8, u8),
+        VariantB(u8, bool),
+    }
+
+    enum Complex {
+        A(u8),
+        B(u8, bool),
+        C(u8, i32, f64),
+        D(E, bool),
+    }
+
+    fn match_test() {
+        let ce = Complex::B(8, false);
+        let aa = 0_u8;
+        let bb = false;
+
+        let _: Complex = ce;
+
+        // Don't trigger
+        let _: Complex = match ce {
+            Complex::A(_) => Complex::A(aa),
+            Complex::B(_, b) => Complex::B(aa, b),
+            Complex::C(_, b, _) => Complex::C(aa, b, 64_f64),
+            Complex::D(e, b) => Complex::D(e, b),
+        };
+
+        // Don't trigger
+        let _: Complex = match ce {
+            Complex::A(a) => Complex::A(a),
+            Complex::B(a, _) => Complex::B(a, bb),
+            Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64),
+            _ => ce,
+        };
+    }
+}
+
+/// Lint triggered when type coercions happen.
+/// Do NOT trigger on any of these.
+mod issue8551 {
+    trait Trait {}
+    struct Struct;
+    impl Trait for Struct {}
+
+    fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> {
+        match s {
+            Some(s) => Some(s),
+            None => None,
+        }
+    }
+
+    fn lint_tests() {
+        let option: Option<&Struct> = None;
+        let _: Option<&dyn Trait> = match option {
+            Some(s) => Some(s),
+            None => None,
+        };
+
+        let _: Option<&dyn Trait> = if true {
+            match option {
+                Some(s) => Some(s),
+                None => None,
+            }
+        } else {
+            None
+        };
+
+        let result: Result<&Struct, i32> = Err(0);
+        let _: Result<&dyn Trait, i32> = match result {
+            Ok(s) => Ok(s),
+            Err(e) => Err(e),
+        };
+
+        let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None };
+    }
+}
+
+trait Tr {
+    fn as_mut(&mut self) -> Result<&mut i32, &mut i32>;
+}
+impl Tr for Result<i32, i32> {
+    fn as_mut(&mut self) -> Result<&mut i32, &mut i32> {
+        match self {
+            Ok(x) => Ok(x),
+            Err(e) => Err(e),
+        }
+    }
+}
+
 fn main() {}
index 36649de35a6030f91a1716c9d23844689981fccf..d210ecff7f1633a8a6e157e643cc9d42725457e9 100644 (file)
@@ -4,33 +4,21 @@
 #![allow(dead_code)]
 
 #[derive(Clone, Copy)]
-enum Choice {
+enum Simple {
     A,
     B,
     C,
     D,
 }
 
-#[allow(unused_mut)]
 fn useless_match() {
-    let mut i = 10;
+    let i = 10;
     let _: i32 = match i {
         0 => 0,
         1 => 1,
         2 => 2,
         _ => i,
     };
-    let _: i32 = match i {
-        0 => 0,
-        1 => 1,
-        ref i => *i,
-    };
-    let mut _i_mut = match i {
-        0 => 0,
-        1 => 1,
-        ref mut i => *i,
-    };
-
     let s = "test";
     let _: &str = match s {
         "a" => "a",
@@ -39,25 +27,26 @@ fn useless_match() {
     };
 }
 
-fn custom_type_match(se: Choice) {
-    let _: Choice = match se {
-        Choice::A => Choice::A,
-        Choice::B => Choice::B,
-        Choice::C => Choice::C,
-        Choice::D => Choice::D,
+fn custom_type_match() {
+    let se = Simple::A;
+    let _: Simple = match se {
+        Simple::A => Simple::A,
+        Simple::B => Simple::B,
+        Simple::C => Simple::C,
+        Simple::D => Simple::D,
     };
     // Don't trigger
-    let _: Choice = match se {
-        Choice::A => Choice::A,
-        Choice::B => Choice::B,
-        _ => Choice::C,
+    let _: Simple = match se {
+        Simple::A => Simple::A,
+        Simple::B => Simple::B,
+        _ => Simple::C,
     };
     // Mingled, don't trigger
-    let _: Choice = match se {
-        Choice::A => Choice::B,
-        Choice::B => Choice::C,
-        Choice::C => Choice::D,
-        Choice::D => Choice::A,
+    let _: Simple = match se {
+        Simple::A => Simple::B,
+        Simple::B => Simple::C,
+        Simple::C => Simple::D,
+        Simple::D => Simple::A,
     };
 }
 
@@ -86,37 +75,160 @@ fn result_match() {
         Err(err) => Err(err),
         Ok(a) => Ok(a),
     };
+    // as ref, don't trigger
+    let res = &func_ret_err(0_i32);
+    let _: Result<&i32, &i32> = match *res {
+        Ok(ref x) => Ok(x),
+        Err(ref x) => Err(x),
+    };
 }
 
-fn if_let_option() -> Option<i32> {
-    if let Some(a) = Some(1) { Some(a) } else { None }
+fn if_let_option() {
+    let _ = if let Some(a) = Some(1) { Some(a) } else { None };
+
+    fn do_something() {}
+
+    // Don't trigger
+    let _ = if let Some(a) = Some(1) {
+        Some(a)
+    } else {
+        do_something();
+        None
+    };
+
+    // Don't trigger
+    let _ = if let Some(a) = Some(1) {
+        do_something();
+        Some(a)
+    } else {
+        None
+    };
 }
 
-fn if_let_result(x: Result<(), i32>) {
-    let _: Result<(), i32> = if let Err(e) = x { Err(e) } else { x };
-    let _: Result<(), i32> = if let Ok(val) = x { Ok(val) } else { x };
+fn if_let_result() {
+    let x: Result<i32, i32> = Ok(1);
+    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
-    let _: Result<(), i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
+    let _: Result<i32, i32> = if let Err(e) = Ok(1) { Err(e) } else { x };
 }
 
-fn if_let_custom_enum(x: Choice) {
-    let _: Choice = if let Choice::A = x {
-        Choice::A
-    } else if let Choice::B = x {
-        Choice::B
-    } else if let Choice::C = x {
-        Choice::C
+fn if_let_custom_enum(x: Simple) {
+    let _: Simple = if let Simple::A = x {
+        Simple::A
+    } else if let Simple::B = x {
+        Simple::B
+    } else if let Simple::C = x {
+        Simple::C
     } else {
         x
     };
+
     // Don't trigger
-    let _: Choice = if let Choice::A = x {
-        Choice::A
+    let _: Simple = if let Simple::A = x {
+        Simple::A
     } else if true {
-        Choice::B
+        Simple::B
     } else {
         x
     };
 }
 
+mod issue8542 {
+    #[derive(Clone, Copy)]
+    enum E {
+        VariantA(u8, u8),
+        VariantB(u8, bool),
+    }
+
+    enum Complex {
+        A(u8),
+        B(u8, bool),
+        C(u8, i32, f64),
+        D(E, bool),
+    }
+
+    fn match_test() {
+        let ce = Complex::B(8, false);
+        let aa = 0_u8;
+        let bb = false;
+
+        let _: Complex = match ce {
+            Complex::A(a) => Complex::A(a),
+            Complex::B(a, b) => Complex::B(a, b),
+            Complex::C(a, b, c) => Complex::C(a, b, c),
+            Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b),
+            Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b),
+        };
+
+        // Don't trigger
+        let _: Complex = match ce {
+            Complex::A(_) => Complex::A(aa),
+            Complex::B(_, b) => Complex::B(aa, b),
+            Complex::C(_, b, _) => Complex::C(aa, b, 64_f64),
+            Complex::D(e, b) => Complex::D(e, b),
+        };
+
+        // Don't trigger
+        let _: Complex = match ce {
+            Complex::A(a) => Complex::A(a),
+            Complex::B(a, _) => Complex::B(a, bb),
+            Complex::C(a, _, _) => Complex::C(a, 32_i32, 64_f64),
+            _ => ce,
+        };
+    }
+}
+
+/// Lint triggered when type coercions happen.
+/// Do NOT trigger on any of these.
+mod issue8551 {
+    trait Trait {}
+    struct Struct;
+    impl Trait for Struct {}
+
+    fn optmap(s: Option<&Struct>) -> Option<&dyn Trait> {
+        match s {
+            Some(s) => Some(s),
+            None => None,
+        }
+    }
+
+    fn lint_tests() {
+        let option: Option<&Struct> = None;
+        let _: Option<&dyn Trait> = match option {
+            Some(s) => Some(s),
+            None => None,
+        };
+
+        let _: Option<&dyn Trait> = if true {
+            match option {
+                Some(s) => Some(s),
+                None => None,
+            }
+        } else {
+            None
+        };
+
+        let result: Result<&Struct, i32> = Err(0);
+        let _: Result<&dyn Trait, i32> = match result {
+            Ok(s) => Ok(s),
+            Err(e) => Err(e),
+        };
+
+        let _: Option<&dyn Trait> = if let Some(s) = option { Some(s) } else { None };
+    }
+}
+
+trait Tr {
+    fn as_mut(&mut self) -> Result<&mut i32, &mut i32>;
+}
+impl Tr for Result<i32, i32> {
+    fn as_mut(&mut self) -> Result<&mut i32, &mut i32> {
+        match self {
+            Ok(x) => Ok(x),
+            Err(e) => Err(e),
+        }
+    }
+}
+
 fn main() {}
index ad1525406ade7abd6029b8f85278520b478c768e..34c5226f06057611d9eb87ac5c631b4d241850e8 100644 (file)
@@ -1,5 +1,5 @@
 error: this match expression is unnecessary
-  --> $DIR/needless_match.rs:17:18
+  --> $DIR/needless_match.rs:16:18
    |
 LL |       let _: i32 = match i {
    |  __________________^
@@ -13,29 +13,7 @@ LL | |     };
    = note: `-D clippy::needless-match` implied by `-D warnings`
 
 error: this match expression is unnecessary
-  --> $DIR/needless_match.rs:23:18
-   |
-LL |       let _: i32 = match i {
-   |  __________________^
-LL | |         0 => 0,
-LL | |         1 => 1,
-LL | |         ref i => *i,
-LL | |     };
-   | |_____^ help: replace it with: `i`
-
-error: this match expression is unnecessary
-  --> $DIR/needless_match.rs:28:22
-   |
-LL |       let mut _i_mut = match i {
-   |  ______________________^
-LL | |         0 => 0,
-LL | |         1 => 1,
-LL | |         ref mut i => *i,
-LL | |     };
-   | |_____^ help: replace it with: `i`
-
-error: this match expression is unnecessary
-  --> $DIR/needless_match.rs:35:19
+  --> $DIR/needless_match.rs:23:19
    |
 LL |       let _: &str = match s {
    |  ___________________^
@@ -46,19 +24,19 @@ LL | |     };
    | |_____^ help: replace it with: `s`
 
 error: this match expression is unnecessary
-  --> $DIR/needless_match.rs:43:21
+  --> $DIR/needless_match.rs:32:21
    |
-LL |       let _: Choice = match se {
+LL |       let _: Simple = match se {
    |  _____________________^
-LL | |         Choice::A => Choice::A,
-LL | |         Choice::B => Choice::B,
-LL | |         Choice::C => Choice::C,
-LL | |         Choice::D => Choice::D,
+LL | |         Simple::A => Simple::A,
+LL | |         Simple::B => Simple::B,
+LL | |         Simple::C => Simple::C,
+LL | |         Simple::D => Simple::D,
 LL | |     };
    | |_____^ help: replace it with: `se`
 
 error: this match expression is unnecessary
-  --> $DIR/needless_match.rs:65:26
+  --> $DIR/needless_match.rs:54:26
    |
 LL |       let _: Option<i32> = match x {
    |  __________________________^
@@ -68,7 +46,7 @@ LL | |     };
    | |_____^ help: replace it with: `x`
 
 error: this match expression is unnecessary
-  --> $DIR/needless_match.rs:81:31
+  --> $DIR/needless_match.rs:70:31
    |
 LL |       let _: Result<i32, i32> = match Ok(1) {
    |  _______________________________^
@@ -78,7 +56,7 @@ LL | |     };
    | |_____^ help: replace it with: `Ok(1)`
 
 error: this match expression is unnecessary
-  --> $DIR/needless_match.rs:85:31
+  --> $DIR/needless_match.rs:74:31
    |
 LL |       let _: Result<i32, i32> = match func_ret_err(0_i32) {
    |  _______________________________^
@@ -88,35 +66,48 @@ LL | |     };
    | |_____^ help: replace it with: `func_ret_err(0_i32)`
 
 error: this if-let expression is unnecessary
-  --> $DIR/needless_match.rs:92:5
+  --> $DIR/needless_match.rs:87:13
    |
-LL |     if let Some(a) = Some(1) { Some(a) } else { None }
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)`
+LL |     let _ = if let Some(a) = Some(1) { Some(a) } else { None };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `Some(1)`
 
 error: this if-let expression is unnecessary
-  --> $DIR/needless_match.rs:96:30
+  --> $DIR/needless_match.rs:110:31
    |
-LL |     let _: Result<(), i32> = if let Err(e) = x { Err(e) } else { x };
-   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
+LL |     let _: Result<i32, i32> = if let Err(e) = x { Err(e) } else { x };
+   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
 
 error: this if-let expression is unnecessary
-  --> $DIR/needless_match.rs:97:30
+  --> $DIR/needless_match.rs:111:31
    |
-LL |     let _: Result<(), i32> = if let Ok(val) = x { Ok(val) } else { x };
-   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x`
+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:103:21
+  --> $DIR/needless_match.rs:117:21
    |
-LL |       let _: Choice = if let Choice::A = x {
+LL |       let _: Simple = if let Simple::A = x {
    |  _____________________^
-LL | |         Choice::A
-LL | |     } else if let Choice::B = x {
-LL | |         Choice::B
+LL | |         Simple::A
+LL | |     } else if let Simple::B = x {
+LL | |         Simple::B
 ...  |
 LL | |         x
 LL | |     };
    | |_____^ help: replace it with: `x`
 
-error: aborting due to 12 previous errors
+error: this match expression is unnecessary
+  --> $DIR/needless_match.rs:156:26
+   |
+LL |           let _: Complex = match ce {
+   |  __________________________^
+LL | |             Complex::A(a) => Complex::A(a),
+LL | |             Complex::B(a, b) => Complex::B(a, b),
+LL | |             Complex::C(a, b, c) => Complex::C(a, b, c),
+LL | |             Complex::D(E::VariantA(ea, eb), b) => Complex::D(E::VariantA(ea, eb), b),
+LL | |             Complex::D(E::VariantB(ea, eb), b) => Complex::D(E::VariantB(ea, eb), b),
+LL | |         };
+   | |_________^ help: replace it with: `ce`
+
+error: aborting due to 11 previous errors
 
index d721452ae88803431fd289feb9e223100b16f3ea..c09b07db3dca9787a2c6d585057dcf188e39b508 100644 (file)
@@ -1,13 +1,41 @@
 // run-rustfix
 
-#[warn(clippy::needless_option_as_deref)]
+#![allow(unused)]
+#![warn(clippy::needless_option_as_deref)]
 
 fn main() {
     // should lint
     let _: Option<&usize> = Some(&1);
     let _: Option<&mut usize> = Some(&mut 1);
 
+    let mut y = 0;
+    let mut x = Some(&mut y);
+    let _ = x;
+
     // should not lint
     let _ = Some(Box::new(1)).as_deref();
     let _ = Some(Box::new(1)).as_deref_mut();
+
+    // #7846
+    let mut i = 0;
+    let mut opt_vec = vec![Some(&mut i)];
+    opt_vec[0].as_deref_mut().unwrap();
+
+    let mut i = 0;
+    let x = &mut Some(&mut i);
+    (*x).as_deref_mut();
+
+    // #8047
+    let mut y = 0;
+    let mut x = Some(&mut y);
+    x.as_deref_mut();
+    dbg!(x);
+}
+
+struct S<'a> {
+    opt: Option<&'a mut usize>,
+}
+
+fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> {
+    s.opt.as_deref_mut()
 }
index bb15512adf6aa6c656bba664d822be00aa44364b..c3ba27ecccf22fe8c09ca1327c62944f8b72d75b 100644 (file)
@@ -1,13 +1,41 @@
 // run-rustfix
 
-#[warn(clippy::needless_option_as_deref)]
+#![allow(unused)]
+#![warn(clippy::needless_option_as_deref)]
 
 fn main() {
     // should lint
     let _: Option<&usize> = Some(&1).as_deref();
     let _: Option<&mut usize> = Some(&mut 1).as_deref_mut();
 
+    let mut y = 0;
+    let mut x = Some(&mut y);
+    let _ = x.as_deref_mut();
+
     // should not lint
     let _ = Some(Box::new(1)).as_deref();
     let _ = Some(Box::new(1)).as_deref_mut();
+
+    // #7846
+    let mut i = 0;
+    let mut opt_vec = vec![Some(&mut i)];
+    opt_vec[0].as_deref_mut().unwrap();
+
+    let mut i = 0;
+    let x = &mut Some(&mut i);
+    (*x).as_deref_mut();
+
+    // #8047
+    let mut y = 0;
+    let mut x = Some(&mut y);
+    x.as_deref_mut();
+    dbg!(x);
+}
+
+struct S<'a> {
+    opt: Option<&'a mut usize>,
+}
+
+fn from_field<'a>(s: &'a mut S<'a>) -> Option<&'a mut usize> {
+    s.opt.as_deref_mut()
 }
index 5dd507b4a71e8fef8207e5ac6b1b579cac4f9b28..bc07db5b38ed3ffcb1f161dea22ad85445811857 100644 (file)
@@ -1,5 +1,5 @@
 error: derefed type is same as origin
-  --> $DIR/needless_option_as_deref.rs:7:29
+  --> $DIR/needless_option_as_deref.rs:8:29
    |
 LL |     let _: Option<&usize> = Some(&1).as_deref();
    |                             ^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&1)`
@@ -7,10 +7,16 @@ LL |     let _: Option<&usize> = Some(&1).as_deref();
    = note: `-D clippy::needless-option-as-deref` implied by `-D warnings`
 
 error: derefed type is same as origin
-  --> $DIR/needless_option_as_deref.rs:8:33
+  --> $DIR/needless_option_as_deref.rs:9:33
    |
 LL |     let _: Option<&mut usize> = Some(&mut 1).as_deref_mut();
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `Some(&mut 1)`
 
-error: aborting due to 2 previous errors
+error: derefed type is same as origin
+  --> $DIR/needless_option_as_deref.rs:13:13
+   |
+LL |     let _ = x.as_deref_mut();
+   |             ^^^^^^^^^^^^^^^^ help: try this: `x`
+
+error: aborting due to 3 previous errors
 
index 5427c88faf348e3c3399253280b187f7e42599e0..7ece66a1ccb6f9117df23ed1cb99f8ec1a60ecd8 100644 (file)
@@ -78,7 +78,7 @@ extern "rust-call" fn call_once(self, (who,): (&str,)) -> Self::Output {
     }
 }
 
-struct GreetStruct3 {}
+struct GreetStruct3;
 
 impl FnOnce<(&str,)> for GreetStruct3 {
     type Output = ();
index 7d29445e66c8b97b8730f096f45db5eb3a321255..1290bd8efebd287eda8df11864090b9202a229ba 100644 (file)
@@ -80,6 +80,9 @@ fn option_map_unit_fn() {
 
     if let Some(ref value) = x.field { do_nothing(value + captured) }
 
-    if let Some(a) = option() { do_nothing(a) }}
+    if let Some(a) = option() { do_nothing(a) }
+
+    if let Some(value) = option() { println!("{:?}", value) }
+}
 
 fn main() {}
index b6f834f686f9f34cf68ef9f27dc131a226664ee6..f3e5b62c65b7f769bb0812d8460a2791b0a14c65 100644 (file)
@@ -80,6 +80,9 @@ fn option_map_unit_fn() {
 
     x.field.map(|ref value| { do_nothing(value + captured) });
 
-    option().map(do_nothing);}
+    option().map(do_nothing);
+
+    option().map(|value| println!("{:?}", value));
+}
 
 fn main() {}
index 8abdbcafb6e935ed9ae422fc893e801f27363723..ab2a294a060f0c2507b81f1eedd6288e4b486f3d 100644 (file)
@@ -139,10 +139,18 @@ LL |     x.field.map(|ref value| { do_nothing(value + captured) });
 error: called `map(f)` on an `Option` value where `f` is a function that returns the unit type `()`
   --> $DIR/option_map_unit_fn_fixable.rs:83:5
    |
-LL |     option().map(do_nothing);}
+LL |     option().map(do_nothing);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^-
    |     |
    |     help: try this: `if let Some(a) = option() { do_nothing(a) }`
 
-error: aborting due to 18 previous errors
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+  --> $DIR/option_map_unit_fn_fixable.rs:85:5
+   |
+LL |     option().map(|value| println!("{:?}", value));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+   |     |
+   |     help: try this: `if let Some(value) = option() { println!("{:?}", value) }`
+
+error: aborting due to 19 previous errors
 
index 27d4b795a5eeb8fd31307b952f03006c23068784..6e0d5a87f6807b012fcc55428b5b0aee61882301 100644 (file)
@@ -3,7 +3,7 @@
 #![warn(clippy::or_then_unwrap)]
 #![allow(clippy::map_identity)]
 
-struct SomeStruct {}
+struct SomeStruct;
 impl SomeStruct {
     fn or(self, _: Option<Self>) -> Self {
         self
@@ -11,7 +11,7 @@ impl SomeStruct {
     fn unwrap(&self) {}
 }
 
-struct SomeOtherStruct {}
+struct SomeOtherStruct;
 impl SomeOtherStruct {
     fn or(self) -> Self {
         self
index 0dab5ae2f1c04167c996b5e82e9975a27efe615c..e406a71d46d00d377024971cee471851e0c3a063 100644 (file)
@@ -3,7 +3,7 @@
 #![warn(clippy::or_then_unwrap)]
 #![allow(clippy::map_identity)]
 
-struct SomeStruct {}
+struct SomeStruct;
 impl SomeStruct {
     fn or(self, _: Option<Self>) -> Self {
         self
@@ -11,7 +11,7 @@ fn or(self, _: Option<Self>) -> Self {
     fn unwrap(&self) {}
 }
 
-struct SomeOtherStruct {}
+struct SomeOtherStruct;
 impl SomeOtherStruct {
     fn or(self) -> Self {
         self
index 7338064646244ca7611a01d73c61203a15a0631f..12a0c776ae2fd6feb90371be57be32ddd59379de 100644 (file)
@@ -1,8 +1,23 @@
-#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]
 #![allow(clippy::assertions_on_constants, clippy::eq_op)]
+#![feature(inline_const)]
+#![warn(clippy::unimplemented, clippy::unreachable, clippy::todo, clippy::panic)]
 
 extern crate core;
 
+const _: () = {
+    if 1 == 0 {
+        panic!("A balanced diet means a cupcake in each hand");
+    }
+};
+
+fn inline_const() {
+    let _ = const {
+        if 1 == 0 {
+            panic!("When nothing goes right, go left")
+        }
+    };
+}
+
 fn panic() {
     let a = 2;
     panic!();
index bfd1c7a380149b2cb89f6480f96b6ed1d1669808..4ceb6d1440f61c58d718ae051124d8e7c12285c4 100644 (file)
@@ -1,5 +1,5 @@
 error: `panic` should not be present in production code
-  --> $DIR/panicking_macros.rs:8:5
+  --> $DIR/panicking_macros.rs:23:5
    |
 LL |     panic!();
    |     ^^^^^^^^
@@ -7,19 +7,19 @@ LL |     panic!();
    = note: `-D clippy::panic` implied by `-D warnings`
 
 error: `panic` should not be present in production code
-  --> $DIR/panicking_macros.rs:9:5
+  --> $DIR/panicking_macros.rs:24:5
    |
 LL |     panic!("message");
    |     ^^^^^^^^^^^^^^^^^
 
 error: `panic` should not be present in production code
-  --> $DIR/panicking_macros.rs:10:5
+  --> $DIR/panicking_macros.rs:25:5
    |
 LL |     panic!("{} {}", "panic with", "multiple arguments");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `todo` should not be present in production code
-  --> $DIR/panicking_macros.rs:16:5
+  --> $DIR/panicking_macros.rs:31:5
    |
 LL |     todo!();
    |     ^^^^^^^
@@ -27,19 +27,19 @@ LL |     todo!();
    = note: `-D clippy::todo` implied by `-D warnings`
 
 error: `todo` should not be present in production code
-  --> $DIR/panicking_macros.rs:17:5
+  --> $DIR/panicking_macros.rs:32:5
    |
 LL |     todo!("message");
    |     ^^^^^^^^^^^^^^^^
 
 error: `todo` should not be present in production code
-  --> $DIR/panicking_macros.rs:18:5
+  --> $DIR/panicking_macros.rs:33:5
    |
 LL |     todo!("{} {}", "panic with", "multiple arguments");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `unimplemented` should not be present in production code
-  --> $DIR/panicking_macros.rs:24:5
+  --> $DIR/panicking_macros.rs:39:5
    |
 LL |     unimplemented!();
    |     ^^^^^^^^^^^^^^^^
@@ -47,19 +47,19 @@ LL |     unimplemented!();
    = note: `-D clippy::unimplemented` implied by `-D warnings`
 
 error: `unimplemented` should not be present in production code
-  --> $DIR/panicking_macros.rs:25:5
+  --> $DIR/panicking_macros.rs:40:5
    |
 LL |     unimplemented!("message");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `unimplemented` should not be present in production code
-  --> $DIR/panicking_macros.rs:26:5
+  --> $DIR/panicking_macros.rs:41:5
    |
 LL |     unimplemented!("{} {}", "panic with", "multiple arguments");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: usage of the `unreachable!` macro
-  --> $DIR/panicking_macros.rs:32:5
+  --> $DIR/panicking_macros.rs:47:5
    |
 LL |     unreachable!();
    |     ^^^^^^^^^^^^^^
@@ -67,37 +67,37 @@ LL |     unreachable!();
    = note: `-D clippy::unreachable` implied by `-D warnings`
 
 error: usage of the `unreachable!` macro
-  --> $DIR/panicking_macros.rs:33:5
+  --> $DIR/panicking_macros.rs:48:5
    |
 LL |     unreachable!("message");
    |     ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: usage of the `unreachable!` macro
-  --> $DIR/panicking_macros.rs:34:5
+  --> $DIR/panicking_macros.rs:49:5
    |
 LL |     unreachable!("{} {}", "panic with", "multiple arguments");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `panic` should not be present in production code
-  --> $DIR/panicking_macros.rs:40:5
+  --> $DIR/panicking_macros.rs:55:5
    |
 LL |     panic!();
    |     ^^^^^^^^
 
 error: `todo` should not be present in production code
-  --> $DIR/panicking_macros.rs:41:5
+  --> $DIR/panicking_macros.rs:56:5
    |
 LL |     todo!();
    |     ^^^^^^^
 
 error: `unimplemented` should not be present in production code
-  --> $DIR/panicking_macros.rs:42:5
+  --> $DIR/panicking_macros.rs:57:5
    |
 LL |     unimplemented!();
    |     ^^^^^^^^^^^^^^^^
 
 error: usage of the `unreachable!` macro
-  --> $DIR/panicking_macros.rs:43:5
+  --> $DIR/panicking_macros.rs:58:5
    |
 LL |     unreachable!();
    |     ^^^^^^^^^^^^^^
index 03dd938a2339e57cda8eca5088c8b06a3c5d03ab..814bbc7af713b77783dbcc741888ed95868ad2f9 100644 (file)
@@ -112,7 +112,7 @@ fn allowed(
     ) {
     }
 
-    struct S {}
+    struct S;
     impl S {
         fn allowed(
             #[allow(clippy::ptr_arg)] _v: &Vec<u32>,
index 9241bf7ed74025b8508ace83be7a2819fe66d09e..f72fc77ab9977dda9e161eb1714b2f912647ca88 100644 (file)
@@ -66,7 +66,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 
 // Check for use of self as Display, in Display impl
 // Triggers on direct use of self
-struct G {}
+struct G;
 
 impl std::fmt::Display for G {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -75,7 +75,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 }
 
 // Triggers on reference to self
-struct H {}
+struct H;
 
 impl std::fmt::Display for H {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -90,7 +90,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 }
 
 // Triggers on multiple reference to self
-struct H2 {}
+struct H2;
 
 impl std::fmt::Display for H2 {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -99,7 +99,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 }
 
 // Doesn't trigger on correct deref
-struct I {}
+struct I;
 
 impl std::ops::Deref for I {
     type Target = str;
@@ -122,7 +122,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 }
 
 // Doesn't trigger on multiple correct deref
-struct I2 {}
+struct I2;
 
 impl std::ops::Deref for I2 {
     type Target = str;
@@ -139,7 +139,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 }
 
 // Doesn't trigger on multiple correct deref
-struct I3 {}
+struct I3;
 
 impl std::ops::Deref for I3 {
     type Target = str;
@@ -156,7 +156,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 }
 
 // Does trigger when deref resolves to self
-struct J {}
+struct J;
 
 impl std::ops::Deref for J {
     type Target = str;
@@ -178,7 +178,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
     }
 }
 
-struct J2 {}
+struct J2;
 
 impl std::ops::Deref for J2 {
     type Target = str;
@@ -194,7 +194,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
     }
 }
 
-struct J3 {}
+struct J3;
 
 impl std::ops::Deref for J3 {
     type Target = str;
@@ -210,7 +210,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
     }
 }
 
-struct J4 {}
+struct J4;
 
 impl std::ops::Deref for J4 {
     type Target = str;
@@ -227,7 +227,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 }
 
 // Doesn't trigger on Debug from Display
-struct K {}
+struct K;
 
 impl std::fmt::Debug for K {
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
@@ -242,7 +242,7 @@ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
 }
 
 // Doesn't trigger on Display from Debug
-struct K2 {}
+struct K2;
 
 impl std::fmt::Debug for K2 {
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
index 52fbc91e325555a9c808f57a47e76cc48e893363..80f94e5f3cbbb278e4d333f7b1887ff3d51ec81e 100644 (file)
@@ -3,7 +3,7 @@
 #![allow(clippy::blacklisted_name, unused_variables, dead_code)]
 #![allow(unused_imports)]
 
-pub struct MyStruct {}
+pub struct MyStruct;
 
 pub struct SubT<T> {
     foo: T,
index ef089b25f47fdae22b8d18c322320fd1cd6f7200..e7ed84731c02e4d64ec6e45cd5782d0e632b2416 100644 (file)
@@ -4,7 +4,7 @@
 #![allow(clippy::blacklisted_name, unused_variables, dead_code)]
 #![allow(unused_imports)]
 
-pub struct MyStruct {}
+pub struct MyStruct;
 
 pub struct SubT<T> {
     foo: T,
index fefa87721d720b9deeb77fe4d27657abb5783b88..de763f98b5c89fb55d344bff8b836fde26f2f916 100644 (file)
@@ -4,7 +4,7 @@
 #![allow(clippy::blacklisted_name, unused_variables, dead_code)]
 #![allow(unused_imports)]
 
-pub struct MyStruct {}
+pub struct MyStruct;
 
 pub struct SubT<T> {
     foo: T,
index 16b40dcd902869d7184cb1d6b384139166d04380..1525f6a93dfdd4dcac0492dad3adbf63e8f621f5 100644 (file)
@@ -1,7 +1,7 @@
 // run-rustfix
 // rustfix-only-machine-applicable
 
-#![allow(clippy::implicit_clone)]
+#![allow(clippy::implicit_clone, clippy::drop_non_drop)]
 use std::ffi::OsString;
 use std::path::Path;
 
index bd3d7365229fb83ccd13ca555e53d9941f867c43..2f82aefd928309c23eee5d3c7a0540a2f5335b5a 100644 (file)
@@ -1,7 +1,7 @@
 // run-rustfix
 // rustfix-only-machine-applicable
 
-#![allow(clippy::implicit_clone)]
+#![allow(clippy::implicit_clone, clippy::drop_non_drop)]
 use std::ffi::OsString;
 use std::path::Path;
 
index 921249606ad2716da28f748c27da23342d52123d..acc8f1e25b6ed3a937186d5146275271ad46c0d8 100644 (file)
@@ -3,7 +3,7 @@
 #![allow(unused)]
 
 #[derive(Debug)]
-struct Foo {}
+struct Foo;
 
 const VAR_ONE: &str = "Test constant #1"; // ERROR Consider removing 'static.
 
index 4d4b249d076ffca81ecd13eca4022b2e960c5c38..f2f0f78659c936516fab442fd624e1ba7f5164a0 100644 (file)
@@ -3,7 +3,7 @@
 #![allow(unused)]
 
 #[derive(Debug)]
-struct Foo {}
+struct Foo;
 
 const VAR_ONE: &'static str = "Test constant #1"; // ERROR Consider removing 'static.
 
index 631042c616bc0cd261579f32aa06c43e9526b151..14c331f67e739c34ca722ff0776a83fbe88f5566 100644 (file)
@@ -75,6 +75,8 @@ fn result_map_unit_fn() {
 
 
     if let Ok(ref value) = x.field { do_nothing(value + captured) }
+
+    if let Ok(value) = x.field { println!("{:?}", value) }
 }
 
 fn main() {}
index 679eb2326268c7a62fba3a04b228d9bbf4a827ee..8b0fca9ece1a375d45e532a5eda015265db5a383 100644 (file)
@@ -75,6 +75,8 @@ fn result_map_unit_fn() {
 
 
     x.field.map(|ref value| { do_nothing(value + captured) });
+
+    x.field.map(|value| println!("{:?}", value));
 }
 
 fn main() {}
index 4f3a8c6b7923986359d770df58bdc6e530410983..782febd52644128e0e7e18e4c1f4316efbb33b9e 100644 (file)
@@ -136,5 +136,13 @@ LL |     x.field.map(|ref value| { do_nothing(value + captured) });
    |     |
    |     help: try this: `if let Ok(ref value) = x.field { do_nothing(value + captured) }`
 
-error: aborting due to 17 previous errors
+error: called `map(f)` on an `Result` value where `f` is a closure that returns the unit type `()`
+  --> $DIR/result_map_unit_fn_fixable.rs:79:5
+   |
+LL |     x.field.map(|value| println!("{:?}", value));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+   |     |
+   |     help: try this: `if let Ok(value) = x.field { println!("{:?}", value) }`
+
+error: aborting due to 18 previous errors
 
index 9d420ec672a26da73e827a620c36de4ddeb3bc75..99964f0de075c04a53467f12fd732e2f97a9506e 100644 (file)
@@ -120,7 +120,7 @@ struct A {
     }
     // Fix #5979
     #[derive(Clone)]
-    struct S {}
+    struct S;
 
     trait T {}
     impl T for S {}
index c307afffcb86325b7dac857ef288042e1a5bdce9..63d31ff83f9b5c508f09fa556ba5ee2421bd0cc4 100644 (file)
@@ -6,11 +6,31 @@ fn main() {
     let item1 = 2;
     {
         let item = &item1;
-        println!("{}", item);
+        dbg!(item);
     }
 
     {
         let item = &item1;
-        println!("{:?}", item);
+        dbg!(item);
+    }
+
+    {
+        let item = &(0..5);
+        dbg!(item);
+    }
+
+    {
+        let item = &mut (0..5);
+        dbg!(item);
+    }
+
+    {
+        let item = 0..5;
+        dbg!(item);
+    }
+
+    {
+        let item = 0..5;
+        dbg!(item);
     }
 }
index 2c0c03b7211993ee22d5c5022818eec6842cb283..2cda5a329d254fb496dec9dca41bfa899142ed4c 100644 (file)
@@ -5,10 +5,26 @@
 fn main() {
     let item1 = 2;
     for item in &[item1] {
-        println!("{}", item);
+        dbg!(item);
     }
 
     for item in [item1].iter() {
-        println!("{:?}", item);
+        dbg!(item);
+    }
+
+    for item in &[0..5] {
+        dbg!(item);
+    }
+
+    for item in [0..5].iter_mut() {
+        dbg!(item);
+    }
+
+    for item in [0..5] {
+        dbg!(item);
+    }
+
+    for item in [0..5].into_iter() {
+        dbg!(item);
     }
 }
index f52ca8c5a9b0c35c6cda340763ebc6e4ba74cb17..0aeb8da1a2e23e6dac4c85f1be745ede77c4c830 100644 (file)
@@ -2,7 +2,7 @@ error: for loop over a single element
   --> $DIR/single_element_loop.rs:7:5
    |
 LL | /     for item in &[item1] {
-LL | |         println!("{}", item);
+LL | |         dbg!(item);
 LL | |     }
    | |_____^
    |
@@ -11,7 +11,7 @@ help: try
    |
 LL ~     {
 LL +         let item = &item1;
-LL +         println!("{}", item);
+LL +         dbg!(item);
 LL +     }
    |
 
@@ -19,7 +19,7 @@ error: for loop over a single element
   --> $DIR/single_element_loop.rs:11:5
    |
 LL | /     for item in [item1].iter() {
-LL | |         println!("{:?}", item);
+LL | |         dbg!(item);
 LL | |     }
    | |_____^
    |
@@ -27,9 +27,73 @@ help: try
    |
 LL ~     {
 LL +         let item = &item1;
-LL +         println!("{:?}", item);
+LL +         dbg!(item);
 LL +     }
    |
 
-error: aborting due to 2 previous errors
+error: for loop over a single element
+  --> $DIR/single_element_loop.rs:15:5
+   |
+LL | /     for item in &[0..5] {
+LL | |         dbg!(item);
+LL | |     }
+   | |_____^
+   |
+help: try
+   |
+LL ~     {
+LL +         let item = &(0..5);
+LL +         dbg!(item);
+LL +     }
+   |
+
+error: for loop over a single element
+  --> $DIR/single_element_loop.rs:19:5
+   |
+LL | /     for item in [0..5].iter_mut() {
+LL | |         dbg!(item);
+LL | |     }
+   | |_____^
+   |
+help: try
+   |
+LL ~     {
+LL +         let item = &mut (0..5);
+LL +         dbg!(item);
+LL +     }
+   |
+
+error: for loop over a single element
+  --> $DIR/single_element_loop.rs:23:5
+   |
+LL | /     for item in [0..5] {
+LL | |         dbg!(item);
+LL | |     }
+   | |_____^
+   |
+help: try
+   |
+LL ~     {
+LL +         let item = 0..5;
+LL +         dbg!(item);
+LL +     }
+   |
+
+error: for loop over a single element
+  --> $DIR/single_element_loop.rs:27:5
+   |
+LL | /     for item in [0..5].into_iter() {
+LL | |         dbg!(item);
+LL | |     }
+   | |_____^
+   |
+help: try
+   |
+LL ~     {
+LL +         let item = 0..5;
+LL +         dbg!(item);
+LL +     }
+   |
+
+error: aborting due to 6 previous errors
 
index 21de19a26014755f493dff4dbf1927798c78aa70..f5ca91143af25683625d19ba51efe22d070e5233 100644 (file)
@@ -62,7 +62,7 @@ fn h()
 }
 
 #[derive(Default, Clone)]
-struct Life {}
+struct Life;
 
 impl T for Life {
     // this should not warn
@@ -85,7 +85,7 @@ fn into_group_btreemap<K, V>(self) -> BTreeMap<K, Vec<V>>
     }
 }
 
-struct Foo {}
+struct Foo;
 
 trait FooIter: Iterator<Item = Foo> {
     fn bar()
index 9b681a79aae7a385f922a6c04aaf2cd06b37bd94..5b688ce4d644f766881cf36b74e3648df9c2f061 100644 (file)
@@ -73,6 +73,10 @@ fn crosspointer() {
 fn int_to_char() {
     let _: char = unsafe { std::mem::transmute(0_u32) };
     let _: char = unsafe { std::mem::transmute(0_i32) };
+
+    // These shouldn't warn
+    const _: char = unsafe { std::mem::transmute(0_u32) };
+    const _: char = unsafe { std::mem::transmute(0_i32) };
 }
 
 #[warn(clippy::transmute_int_to_bool)]
@@ -130,9 +134,12 @@ const fn test_const() {
     }
 }
 
-fn bytes_to_str(b: &[u8], mb: &mut [u8]) {
-    let _: &str = unsafe { std::mem::transmute(b) };
+fn bytes_to_str(mb: &mut [u8]) {
+    const B: &[u8] = b"";
+
+    let _: &str = unsafe { std::mem::transmute(B) };
     let _: &mut str = unsafe { std::mem::transmute(mb) };
+    const _: &str = unsafe { std::mem::transmute(B) };
 }
 
 fn main() {}
index 86537153e322897de816064f7b80041c88418c0d..72a386e8fa618f2fe96c5b7f7fc188759c07c161 100644 (file)
@@ -107,7 +107,7 @@ LL |     let _: char = unsafe { std::mem::transmute(0_i32) };
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::char::from_u32(0_i32 as u32).unwrap()`
 
 error: transmute from a `u8` to a `bool`
-  --> $DIR/transmute.rs:80:28
+  --> $DIR/transmute.rs:84:28
    |
 LL |     let _: bool = unsafe { std::mem::transmute(0_u8) };
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0`
@@ -115,7 +115,7 @@ LL |     let _: bool = unsafe { std::mem::transmute(0_u8) };
    = note: `-D clippy::transmute-int-to-bool` implied by `-D warnings`
 
 error: transmute from a `u32` to a `f32`
-  --> $DIR/transmute.rs:86:31
+  --> $DIR/transmute.rs:90:31
    |
 LL |         let _: f32 = unsafe { std::mem::transmute(0_u32) };
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_u32)`
@@ -123,25 +123,25 @@ LL |         let _: f32 = unsafe { std::mem::transmute(0_u32) };
    = note: `-D clippy::transmute-int-to-float` implied by `-D warnings`
 
 error: transmute from a `i32` to a `f32`
-  --> $DIR/transmute.rs:87:31
+  --> $DIR/transmute.rs:91:31
    |
 LL |         let _: f32 = unsafe { std::mem::transmute(0_i32) };
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f32::from_bits(0_i32 as u32)`
 
 error: transmute from a `u64` to a `f64`
-  --> $DIR/transmute.rs:88:31
+  --> $DIR/transmute.rs:92:31
    |
 LL |         let _: f64 = unsafe { std::mem::transmute(0_u64) };
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_u64)`
 
 error: transmute from a `i64` to a `f64`
-  --> $DIR/transmute.rs:89:31
+  --> $DIR/transmute.rs:93:31
    |
 LL |         let _: f64 = unsafe { std::mem::transmute(0_i64) };
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `f64::from_bits(0_i64 as u64)`
 
 error: transmute from a `u8` to a `[u8; 1]`
-  --> $DIR/transmute.rs:109:30
+  --> $DIR/transmute.rs:113:30
    |
 LL |             let _: [u8; 1] = std::mem::transmute(0u8);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()`
@@ -149,96 +149,102 @@ LL |             let _: [u8; 1] = std::mem::transmute(0u8);
    = note: `-D clippy::transmute-num-to-bytes` implied by `-D warnings`
 
 error: transmute from a `u32` to a `[u8; 4]`
-  --> $DIR/transmute.rs:110:30
+  --> $DIR/transmute.rs:114:30
    |
 LL |             let _: [u8; 4] = std::mem::transmute(0u32);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()`
 
 error: transmute from a `u128` to a `[u8; 16]`
-  --> $DIR/transmute.rs:111:31
+  --> $DIR/transmute.rs:115:31
    |
 LL |             let _: [u8; 16] = std::mem::transmute(0u128);
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()`
 
 error: transmute from a `i8` to a `[u8; 1]`
-  --> $DIR/transmute.rs:112:30
+  --> $DIR/transmute.rs:116:30
    |
 LL |             let _: [u8; 1] = std::mem::transmute(0i8);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()`
 
 error: transmute from a `i32` to a `[u8; 4]`
-  --> $DIR/transmute.rs:113:30
+  --> $DIR/transmute.rs:117:30
    |
 LL |             let _: [u8; 4] = std::mem::transmute(0i32);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()`
 
 error: transmute from a `i128` to a `[u8; 16]`
-  --> $DIR/transmute.rs:114:31
+  --> $DIR/transmute.rs:118:31
    |
 LL |             let _: [u8; 16] = std::mem::transmute(0i128);
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()`
 
 error: transmute from a `f32` to a `[u8; 4]`
-  --> $DIR/transmute.rs:115:30
+  --> $DIR/transmute.rs:119:30
    |
 LL |             let _: [u8; 4] = std::mem::transmute(0.0f32);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f32.to_ne_bytes()`
 
 error: transmute from a `f64` to a `[u8; 8]`
-  --> $DIR/transmute.rs:116:30
+  --> $DIR/transmute.rs:120:30
    |
 LL |             let _: [u8; 8] = std::mem::transmute(0.0f64);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0.0f64.to_ne_bytes()`
 
 error: transmute from a `u8` to a `[u8; 1]`
-  --> $DIR/transmute.rs:121:30
+  --> $DIR/transmute.rs:125:30
    |
 LL |             let _: [u8; 1] = std::mem::transmute(0u8);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u8.to_ne_bytes()`
 
 error: transmute from a `u32` to a `[u8; 4]`
-  --> $DIR/transmute.rs:122:30
+  --> $DIR/transmute.rs:126:30
    |
 LL |             let _: [u8; 4] = std::mem::transmute(0u32);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u32.to_ne_bytes()`
 
 error: transmute from a `u128` to a `[u8; 16]`
-  --> $DIR/transmute.rs:123:31
+  --> $DIR/transmute.rs:127:31
    |
 LL |             let _: [u8; 16] = std::mem::transmute(0u128);
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0u128.to_ne_bytes()`
 
 error: transmute from a `i8` to a `[u8; 1]`
-  --> $DIR/transmute.rs:124:30
+  --> $DIR/transmute.rs:128:30
    |
 LL |             let _: [u8; 1] = std::mem::transmute(0i8);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i8.to_ne_bytes()`
 
 error: transmute from a `i32` to a `[u8; 4]`
-  --> $DIR/transmute.rs:125:30
+  --> $DIR/transmute.rs:129:30
    |
 LL |             let _: [u8; 4] = std::mem::transmute(0i32);
    |                              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i32.to_ne_bytes()`
 
 error: transmute from a `i128` to a `[u8; 16]`
-  --> $DIR/transmute.rs:126:31
+  --> $DIR/transmute.rs:130:31
    |
 LL |             let _: [u8; 16] = std::mem::transmute(0i128);
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `to_ne_bytes()`: `0i128.to_ne_bytes()`
 
 error: transmute from a `&[u8]` to a `&str`
-  --> $DIR/transmute.rs:134:28
+  --> $DIR/transmute.rs:140:28
    |
-LL |     let _: &str = unsafe { std::mem::transmute(b) };
-   |                            ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(b).unwrap()`
+LL |     let _: &str = unsafe { std::mem::transmute(B) };
+   |                            ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()`
    |
    = note: `-D clippy::transmute-bytes-to-str` implied by `-D warnings`
 
 error: transmute from a `&mut [u8]` to a `&mut str`
-  --> $DIR/transmute.rs:135:32
+  --> $DIR/transmute.rs:141:32
    |
 LL |     let _: &mut str = unsafe { std::mem::transmute(mb) };
    |                                ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()`
 
-error: aborting due to 38 previous errors
+error: transmute from a `&[u8]` to a `&str`
+  --> $DIR/transmute.rs:142:30
+   |
+LL |     const _: &str = unsafe { std::mem::transmute(B) };
+   |                              ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)`
+
+error: aborting due to 39 previous errors
 
index 380303d8152aae2bfc56d37e7a09ddf660f46302..afa337c45f41766033abe0d1d85b2694f7fa11f6 100644 (file)
@@ -1,5 +1,9 @@
+// aux-build:proc_macro_unsafe.rs
+
 #![warn(clippy::undocumented_unsafe_blocks)]
 
+extern crate proc_macro_unsafe;
+
 // Valid comments
 
 fn nested_local() {
@@ -89,11 +93,6 @@ fn block_comment_newlines() {
     unsafe {}
 }
 
-#[rustfmt::skip]
-fn inline_block_comment() {
-    /* Safety: */unsafe {}
-}
-
 fn block_comment_with_extras() {
     /* This is a description
      * SAFETY:
@@ -209,8 +208,54 @@ fn local_nest() {
     let _ = [(42, unsafe {}, unsafe {}), (52, unsafe {}, unsafe {})];
 }
 
+fn in_fn_call(x: *const u32) {
+    fn f(x: u32) {}
+
+    // Safety: reason
+    f(unsafe { *x });
+}
+
+fn multi_in_fn_call(x: *const u32) {
+    fn f(x: u32, y: u32) {}
+
+    // Safety: reason
+    f(unsafe { *x }, unsafe { *x });
+}
+
+fn in_multiline_fn_call(x: *const u32) {
+    fn f(x: u32, y: u32) {}
+
+    f(
+        // Safety: reason
+        unsafe { *x },
+        0,
+    );
+}
+
+fn in_macro_call(x: *const u32) {
+    // Safety: reason
+    println!("{}", unsafe { *x });
+}
+
+fn in_multiline_macro_call(x: *const u32) {
+    println!(
+        "{}",
+        // Safety: reason
+        unsafe { *x },
+    );
+}
+
+fn from_proc_macro() {
+    proc_macro_unsafe::unsafe_block!(token);
+}
+
 // Invalid comments
 
+#[rustfmt::skip]
+fn inline_block_comment() {
+    /* Safety: */ unsafe {}
+}
+
 fn no_comment() {
     unsafe {}
 }
index f69d0da54e0d6cf4fdb21a80aa73244b5d38e7c5..856a07fd31685e3170eba2ff3baf47221494cf01 100644 (file)
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:215:5
+  --> $DIR/undocumented_unsafe_blocks.rs:256:19
+   |
+LL |     /* Safety: */ unsafe {}
+   |                   ^^^^^^^^^
+   |
+   = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
+   = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+  --> $DIR/undocumented_unsafe_blocks.rs:260:5
    |
 LL |     unsafe {}
    |     ^^^^^^^^^
    |
-   = note: `-D clippy::undocumented-unsafe-blocks` implied by `-D warnings`
-help: consider adding a safety comment
+   = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+  --> $DIR/undocumented_unsafe_blocks.rs:264:14
    |
-LL ~     // SAFETY: ...
-LL +     unsafe {}
+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:219:5
+  --> $DIR/undocumented_unsafe_blocks.rs:264:29
    |
 LL |     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                             ^^^^^^^^^^^^^
    |
-help: consider adding a safety comment
+   = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+  --> $DIR/undocumented_unsafe_blocks.rs:264:48
    |
-LL ~     // SAFETY: ...
-LL +     let _ = [unsafe { 14 }, unsafe { 15 }, 42, unsafe { 16 }];
+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:223:5
+  --> $DIR/undocumented_unsafe_blocks.rs:268:18
    |
 LL |     let _ = (42, unsafe {}, "test", unsafe {});
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                  ^^^^^^^^^
    |
-help: consider adding a safety comment
+   = help: consider adding a safety comment on the preceding line
+
+error: unsafe block missing a safety comment
+  --> $DIR/undocumented_unsafe_blocks.rs:268:37
    |
-LL ~     // SAFETY: ...
-LL +     let _ = (42, unsafe {}, "test", unsafe {});
+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:227:5
+  --> $DIR/undocumented_unsafe_blocks.rs:272:14
    |
 LL |     let _ = *unsafe { &42 };
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: consider adding a safety comment
-   |
-LL ~     // SAFETY: ...
-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:232:5
+  --> $DIR/undocumented_unsafe_blocks.rs:277:19
    |
 LL |     let _ = match unsafe {} {
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: consider adding a safety comment
-   |
-LL ~     // SAFETY: ...
-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:238:5
+  --> $DIR/undocumented_unsafe_blocks.rs:283:14
    |
 LL |     let _ = &unsafe {};
-   |     ^^^^^^^^^^^^^^^^^^^
-   |
-help: consider adding a safety comment
-   |
-LL ~     // SAFETY: ...
-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:242:5
+  --> $DIR/undocumented_unsafe_blocks.rs:287:14
    |
 LL |     let _ = [unsafe {}; 5];
-   |     ^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: consider adding a safety comment
-   |
-LL ~     // SAFETY: ...
-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:246:5
+  --> $DIR/undocumented_unsafe_blocks.rs:291:13
    |
 LL |     let _ = unsafe {};
-   |     ^^^^^^^^^^^^^^^^^^
-   |
-help: consider adding a safety comment
-   |
-LL ~     // SAFETY: ...
-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:256:8
+  --> $DIR/undocumented_unsafe_blocks.rs:301:8
    |
 LL |     t!(unsafe {});
    |        ^^^^^^^^^
    |
-help: consider adding a safety comment
-   |
-LL ~     t!(// SAFETY: ...
-LL ~     unsafe {});
-   |
+   = help: consider adding a safety comment on the preceding line
 
-error: unsafe block in macro expansion missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:262:13
+error: unsafe block missing a safety comment
+  --> $DIR/undocumented_unsafe_blocks.rs:307:13
    |
 LL |             unsafe {}
    |             ^^^^^^^^^
@@ -116,56 +112,40 @@ LL |             unsafe {}
 LL |     t!();
    |     ---- in this macro invocation
    |
-   = help: consider adding a safety comment in the macro definition
+   = help: consider adding a safety comment on the preceding line
    = 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:270:5
+  --> $DIR/undocumented_unsafe_blocks.rs:315:5
    |
 LL |     unsafe {} // SAFETY:
    |     ^^^^^^^^^
    |
-help: consider adding a safety comment
-   |
-LL ~     // SAFETY: ...
-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:274:5
+  --> $DIR/undocumented_unsafe_blocks.rs:319:5
    |
 LL |     unsafe {
    |     ^^^^^^^^
    |
-help: consider adding a safety comment
-   |
-LL ~     // SAFETY: ...
-LL +     unsafe {
-   |
+   = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:284:5
+  --> $DIR/undocumented_unsafe_blocks.rs:329:5
    |
 LL |     unsafe {};
    |     ^^^^^^^^^
    |
-help: consider adding a safety comment
-   |
-LL ~     // SAFETY: ...
-LL ~     unsafe {};
-   |
+   = help: consider adding a safety comment on the preceding line
 
 error: unsafe block missing a safety comment
-  --> $DIR/undocumented_unsafe_blocks.rs:288:20
+  --> $DIR/undocumented_unsafe_blocks.rs:333:20
    |
 LL |     println!("{}", unsafe { String::from_utf8_unchecked(vec![]) });
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-help: consider adding a safety comment
-   |
-LL ~     println!("{}", // SAFETY: ...
-LL ~     unsafe { String::from_utf8_unchecked(vec![]) });
-   |
+   = help: consider adding a safety comment on the preceding line
 
-error: aborting due to 14 previous errors
+error: aborting due to 18 previous errors
 
index b77c19f2ba5961e30409af2c95dd9640978f9ba3..62c3e9636866a5a67492f339ca706b97aea73c31 100644 (file)
@@ -30,4 +30,10 @@ pub fn $a() -> $b {
 
     // do not lint cast to cfg-dependant type
     1 as std::os::raw::c_char;
+
+    // do not lint cast to alias type
+    1 as I32Alias;
+    &1 as &I32Alias;
 }
+
+type I32Alias = i32;
index 3332f49c80c9758c2f8cbd61fc19d48a7681dfee..36800c5340db21d665070b288040e00958bd612a 100644 (file)
@@ -42,4 +42,9 @@ fn main() {
 
     let _ = -1_i32;
     let _ = -1.0_f32;
+
+    let _ = 1 as I32Alias;
+    let _ = &1 as &I32Alias;
 }
+
+type I32Alias = i32;
index ec01e93877927af3f51217fe4f194bfb6d8d9645..d4b6bb952ab3508261198212ba394d20b5cd3d4c 100644 (file)
@@ -42,4 +42,9 @@ fn main() {
 
     let _ = -1 as i32;
     let _ = -1.0 as f32;
+
+    let _ = 1 as I32Alias;
+    let _ = &1 as &I32Alias;
 }
+
+type I32Alias = i32;
index 690d705573d3f73ae3bba84c7dfb4a97d35e87fb..bafca91917aa431d6747b50a4fee16a4fefe4666 100644 (file)
@@ -6,7 +6,7 @@
 use serde::Deserialize;
 
 #[derive(Deserialize)]
-pub struct A {}
+pub struct A;
 impl A {
     pub unsafe fn new(_a: i32, _b: i32) -> Self {
         Self {}
@@ -14,13 +14,13 @@ pub unsafe fn new(_a: i32, _b: i32) -> Self {
 }
 
 #[derive(Deserialize)]
-pub struct B {}
+pub struct B;
 impl B {
     pub unsafe fn unsafe_method(&self) {}
 }
 
 #[derive(Deserialize)]
-pub struct C {}
+pub struct C;
 impl C {
     pub fn unsafe_block(&self) {
         unsafe {}
@@ -28,7 +28,7 @@ pub fn unsafe_block(&self) {
 }
 
 #[derive(Deserialize)]
-pub struct D {}
+pub struct D;
 impl D {
     pub fn inner_unsafe_fn(&self) {
         unsafe fn inner() {}
@@ -36,7 +36,7 @@ unsafe fn inner() {}
 }
 
 // Does not derive `Deserialize`, should be ignored
-pub struct E {}
+pub struct E;
 impl E {
     pub unsafe fn new(_a: i32, _b: i32) -> Self {
         Self {}
@@ -55,12 +55,12 @@ unsafe fn inner() {}
 
 // Does not have methods using `unsafe`, should be ignored
 #[derive(Deserialize)]
-pub struct F {}
+pub struct F;
 
 // Check that we honor the `allow` attribute on the ADT
 #[allow(clippy::unsafe_derive_deserialize)]
 #[derive(Deserialize)]
-pub struct G {}
+pub struct G;
 impl G {
     pub fn unsafe_block(&self) {
         unsafe {}
index a1f616733bd920a277f43afb1e9ce8bdfd865cc3..cde4e96d668c2c78b5cb79478702db0e66520e46 100644 (file)
@@ -14,8 +14,8 @@
 use std::cell::UnsafeCell as Bombsawayunsafe;
 
 mod mod_with_some_unsafe_things {
-    pub struct Safe {}
-    pub struct Unsafe {}
+    pub struct Safe;
+    pub struct Unsafe;
 }
 
 use mod_with_some_unsafe_things::Unsafe as LieAboutModSafety;
index 7a4bbdda1ab273879245443c17e6275c5a240737..08bf58fec7c3eb0d21b18cb6c64334bfbcb88e53 100644 (file)
@@ -5,7 +5,7 @@ mod unused_self {
     use std::pin::Pin;
     use std::sync::{Arc, Mutex};
 
-    struct A {}
+    struct A;
 
     impl A {
         fn unused_self_move(self) {}
@@ -27,7 +27,7 @@ fn static_method() {}
 }
 
 mod unused_self_allow {
-    struct A {}
+    struct A;
 
     impl A {
         // shouldn't trigger
@@ -35,7 +35,7 @@ impl A {
         fn unused_self_move(self) {}
     }
 
-    struct B {}
+    struct B;
 
     // shouldn't trigger
     #[allow(clippy::unused_self)]
@@ -43,7 +43,7 @@ impl B {
         fn unused_self_move(self) {}
     }
 
-    struct C {}
+    struct C;
 
     #[allow(clippy::unused_self)]
     impl C {
@@ -120,7 +120,7 @@ fn bar(&mut self, x: u8) -> u32 {
 mod not_applicable {
     use std::fmt;
 
-    struct A {}
+    struct A;
 
     impl fmt::Debug for A {
         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
index 9d216f56ae60c66b3a22df7158c40a893f424e08..3e62ffe74feddb270f358cc17785175353e34495 100644 (file)
@@ -16,7 +16,7 @@ extern crate proc_macro_derive;
 fn main() {}
 
 mod use_self {
-    struct Foo {}
+    struct Foo;
 
     impl Foo {
         fn new() -> Self {
@@ -35,7 +35,7 @@ mod use_self {
 }
 
 mod better {
-    struct Foo {}
+    struct Foo;
 
     impl Foo {
         fn new() -> Self {
@@ -123,7 +123,7 @@ mod macros {
         };
     }
 
-    struct Foo {}
+    struct Foo;
 
     impl Foo {
         use_self_expand!(); // Should not lint in local macros
@@ -134,7 +134,7 @@ mod macros {
 }
 
 mod nesting {
-    struct Foo {}
+    struct Foo;
     impl Foo {
         fn foo() {
             #[allow(unused_imports)]
@@ -209,7 +209,7 @@ mod issue3410 {
 #[allow(clippy::no_effect, path_statements)]
 mod rustfix {
     mod nested {
-        pub struct A {}
+        pub struct A;
     }
 
     impl nested::A {
@@ -227,7 +227,7 @@ mod rustfix {
 }
 
 mod issue3567 {
-    struct TestStruct {}
+    struct TestStruct;
     impl TestStruct {
         fn from_something() -> Self {
             Self {}
@@ -248,7 +248,7 @@ mod issue3567 {
 mod paths_created_by_lowering {
     use std::ops::Range;
 
-    struct S {}
+    struct S;
 
     impl S {
         const A: usize = 0;
@@ -382,7 +382,7 @@ mod issue4305 {
 }
 
 mod lint_at_item_level {
-    struct Foo {}
+    struct Foo;
 
     #[allow(clippy::use_self)]
     impl Foo {
@@ -400,7 +400,7 @@ mod lint_at_item_level {
 }
 
 mod lint_at_impl_item_level {
-    struct Foo {}
+    struct Foo;
 
     impl Foo {
         #[allow(clippy::use_self)]
@@ -433,8 +433,8 @@ mod issue4734 {
 mod nested_paths {
     use std::convert::Into;
     mod submod {
-        pub struct B {}
-        pub struct C {}
+        pub struct B;
+        pub struct C;
 
         impl Into<C> for B {
             fn into(self) -> C {
index 5f604fe25e416d93e2fafbf4883b92be539ca0ec..da2faddee12a71327de345e5c7460659853690dc 100644 (file)
@@ -16,7 +16,7 @@
 fn main() {}
 
 mod use_self {
-    struct Foo {}
+    struct Foo;
 
     impl Foo {
         fn new() -> Foo {
@@ -35,7 +35,7 @@ fn default() -> Foo {
 }
 
 mod better {
-    struct Foo {}
+    struct Foo;
 
     impl Foo {
         fn new() -> Self {
@@ -123,7 +123,7 @@ fn new() -> Foo {
         };
     }
 
-    struct Foo {}
+    struct Foo;
 
     impl Foo {
         use_self_expand!(); // Should not lint in local macros
@@ -134,7 +134,7 @@ impl Foo {
 }
 
 mod nesting {
-    struct Foo {}
+    struct Foo;
     impl Foo {
         fn foo() {
             #[allow(unused_imports)]
@@ -209,7 +209,7 @@ fn a(v: Vec<A>) -> Self {
 #[allow(clippy::no_effect, path_statements)]
 mod rustfix {
     mod nested {
-        pub struct A {}
+        pub struct A;
     }
 
     impl nested::A {
@@ -227,7 +227,7 @@ fn fun_2() {
 }
 
 mod issue3567 {
-    struct TestStruct {}
+    struct TestStruct;
     impl TestStruct {
         fn from_something() -> Self {
             Self {}
@@ -248,7 +248,7 @@ fn test() -> TestStruct {
 mod paths_created_by_lowering {
     use std::ops::Range;
 
-    struct S {}
+    struct S;
 
     impl S {
         const A: usize = 0;
@@ -382,7 +382,7 @@ fn from(t: T) -> Self {
 }
 
 mod lint_at_item_level {
-    struct Foo {}
+    struct Foo;
 
     #[allow(clippy::use_self)]
     impl Foo {
@@ -400,7 +400,7 @@ fn default() -> Foo {
 }
 
 mod lint_at_impl_item_level {
-    struct Foo {}
+    struct Foo;
 
     impl Foo {
         #[allow(clippy::use_self)]
@@ -433,8 +433,8 @@ fn from(c: X) -> Self {
 mod nested_paths {
     use std::convert::Into;
     mod submod {
-        pub struct B {}
-        pub struct C {}
+        pub struct B;
+        pub struct C;
 
         impl Into<C> for B {
             fn into(self) -> C {
index a5fcde768f18344ed7684116e0ce5f9aeeb52ee5..ce58a80347b558ef7a46b48b2883def9f0455f71 100644 (file)
@@ -42,7 +42,7 @@ mod a {
     mod b {
         #[allow(dead_code)]
         #[allow(unreachable_pub)]
-        pub struct C {}
+        pub struct C;
     }
 
     #[allow(unreachable_pub)]
index 0396d39e3d54eff2151a4d43964c70cac8d86051..c82bb9ba07fd731efb14a91a954fb3a132cc6423 100644 (file)
@@ -42,7 +42,7 @@ mod a {
     mod b {
         #[allow(dead_code)]
         #[allow(unreachable_pub)]
-        pub struct C {}
+        pub struct C;
     }
 
     #[allow(unreachable_pub)]
index 77102b8cac0c977e9621dc9bcc1bf1c6bcb5c11d..38498ebdcf2c1ed49f0f22ee3a2be6a2c29d078e 100644 (file)
@@ -3,34 +3,32 @@
 #![allow(clippy::single_match_else)]
 
 use rustc_tools_util::VersionInfo;
+use std::fs;
 
 #[test]
 fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() {
+    fn read_version(path: &str) -> String {
+        let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("error reading `{}`: {:?}", path, e));
+        contents
+            .lines()
+            .filter_map(|l| l.split_once('='))
+            .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))
+            .unwrap_or_else(|| panic!("error finding version in `{}`", path))
+            .to_string()
+    }
+
     // do not run this test inside the upstream rustc repo:
     // https://github.com/rust-lang/rust-clippy/issues/6683
     if option_env!("RUSTC_TEST_SUITE").is_some() {
         return;
     }
 
-    let clippy_meta = cargo_metadata::MetadataCommand::new()
-        .no_deps()
-        .exec()
-        .expect("could not obtain cargo metadata");
+    let clippy_version = read_version("Cargo.toml");
+    let clippy_lints_version = read_version("clippy_lints/Cargo.toml");
+    let clippy_utils_version = read_version("clippy_utils/Cargo.toml");
 
-    for krate in &["clippy_lints", "clippy_utils"] {
-        let krate_meta = cargo_metadata::MetadataCommand::new()
-            .current_dir(std::env::current_dir().unwrap().join(krate))
-            .no_deps()
-            .exec()
-            .expect("could not obtain cargo metadata");
-        assert_eq!(krate_meta.packages[0].version, clippy_meta.packages[0].version);
-        for package in &clippy_meta.packages[0].dependencies {
-            if package.name == *krate {
-                assert!(package.req.matches(&krate_meta.packages[0].version));
-                break;
-            }
-        }
-    }
+    assert_eq!(clippy_version, clippy_lints_version);
+    assert_eq!(clippy_version, clippy_utils_version);
 }
 
 #[test]