]> git.lizzy.rs Git - rust.git/commitdiff
Merge commit '0e87918536b9833bbc6c683d1f9d51ee2bf03ef1' into clippyup
authorflip1995 <philipp.krones@embecosm.com>
Thu, 25 Mar 2021 18:29:11 +0000 (19:29 +0100)
committerflip1995 <philipp.krones@embecosm.com>
Thu, 25 Mar 2021 18:29:11 +0000 (19:29 +0100)
415 files changed:
CHANGELOG.md
CONTRIBUTING.md
Cargo.toml
clippy_dev/src/fmt.rs
clippy_dev/src/ide_setup.rs [new file with mode: 0644]
clippy_dev/src/lib.rs
clippy_dev/src/main.rs
clippy_dev/src/ra_setup.rs [deleted file]
clippy_lints/Cargo.toml
clippy_lints/src/approx_const.rs
clippy_lints/src/arithmetic.rs
clippy_lints/src/as_conversions.rs
clippy_lints/src/asm_syntax.rs
clippy_lints/src/assertions_on_constants.rs
clippy_lints/src/assign_ops.rs
clippy_lints/src/async_yields_async.rs
clippy_lints/src/atomic_ordering.rs
clippy_lints/src/attrs.rs
clippy_lints/src/await_holding_invalid.rs
clippy_lints/src/bit_mask.rs
clippy_lints/src/blacklisted_name.rs
clippy_lints/src/blocks_in_if_conditions.rs
clippy_lints/src/booleans.rs
clippy_lints/src/bytecount.rs
clippy_lints/src/cargo_common_metadata.rs
clippy_lints/src/case_sensitive_file_extension_comparisons.rs
clippy_lints/src/casts/cast_lossless.rs
clippy_lints/src/casts/cast_possible_truncation.rs
clippy_lints/src/casts/cast_possible_wrap.rs
clippy_lints/src/casts/cast_precision_loss.rs
clippy_lints/src/casts/cast_ptr_alignment.rs
clippy_lints/src/casts/cast_ref_to_mut.rs
clippy_lints/src/casts/cast_sign_loss.rs
clippy_lints/src/casts/char_lit_as_u8.rs
clippy_lints/src/casts/fn_to_numeric_cast.rs
clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs
clippy_lints/src/casts/mod.rs
clippy_lints/src/casts/ptr_as_ptr.rs
clippy_lints/src/casts/unnecessary_cast.rs
clippy_lints/src/checked_conversions.rs
clippy_lints/src/cognitive_complexity.rs
clippy_lints/src/collapsible_if.rs
clippy_lints/src/collapsible_match.rs
clippy_lints/src/comparison_chain.rs
clippy_lints/src/copies.rs
clippy_lints/src/copy_iterator.rs
clippy_lints/src/create_dir.rs
clippy_lints/src/dbg_macro.rs
clippy_lints/src/default.rs
clippy_lints/src/default_numeric_fallback.rs
clippy_lints/src/dereference.rs
clippy_lints/src/derive.rs
clippy_lints/src/disallowed_method.rs
clippy_lints/src/doc.rs
clippy_lints/src/double_comparison.rs
clippy_lints/src/double_parens.rs
clippy_lints/src/drop_forget_ref.rs
clippy_lints/src/duration_subsec.rs
clippy_lints/src/else_if_without_else.rs
clippy_lints/src/empty_enum.rs
clippy_lints/src/entry.rs
clippy_lints/src/enum_clike.rs
clippy_lints/src/enum_variants.rs
clippy_lints/src/eq_op.rs
clippy_lints/src/erasing_op.rs
clippy_lints/src/escape.rs
clippy_lints/src/eta_reduction.rs
clippy_lints/src/eval_order_dependence.rs
clippy_lints/src/excessive_bools.rs
clippy_lints/src/exhaustive_items.rs
clippy_lints/src/exit.rs
clippy_lints/src/explicit_write.rs
clippy_lints/src/fallible_impl_from.rs
clippy_lints/src/float_equality_without_abs.rs
clippy_lints/src/float_literal.rs
clippy_lints/src/floating_point_arithmetic.rs
clippy_lints/src/format.rs
clippy_lints/src/formatting.rs
clippy_lints/src/from_over_into.rs
clippy_lints/src/from_str_radix_10.rs
clippy_lints/src/functions.rs
clippy_lints/src/future_not_send.rs
clippy_lints/src/get_last_with_len.rs
clippy_lints/src/identity_op.rs
clippy_lints/src/if_let_mutex.rs
clippy_lints/src/if_let_some_result.rs
clippy_lints/src/if_not_else.rs
clippy_lints/src/if_then_some_else_none.rs [new file with mode: 0644]
clippy_lints/src/implicit_return.rs
clippy_lints/src/implicit_saturating_sub.rs
clippy_lints/src/inconsistent_struct_constructor.rs
clippy_lints/src/indexing_slicing.rs
clippy_lints/src/infinite_iter.rs
clippy_lints/src/inherent_impl.rs
clippy_lints/src/inherent_to_string.rs
clippy_lints/src/inline_fn_without_body.rs
clippy_lints/src/int_plus_one.rs
clippy_lints/src/integer_division.rs
clippy_lints/src/items_after_statements.rs
clippy_lints/src/large_const_arrays.rs
clippy_lints/src/large_enum_variant.rs
clippy_lints/src/large_stack_arrays.rs
clippy_lints/src/len_zero.rs
clippy_lints/src/let_if_seq.rs
clippy_lints/src/let_underscore.rs
clippy_lints/src/lib.rs
clippy_lints/src/lifetimes.rs
clippy_lints/src/literal_representation.rs
clippy_lints/src/loops/empty_loop.rs
clippy_lints/src/loops/explicit_counter_loop.rs
clippy_lints/src/loops/explicit_into_iter_loop.rs
clippy_lints/src/loops/explicit_iter_loop.rs
clippy_lints/src/loops/for_kv_map.rs
clippy_lints/src/loops/for_loops_over_fallibles.rs
clippy_lints/src/loops/iter_next_loop.rs
clippy_lints/src/loops/manual_flatten.rs
clippy_lints/src/loops/manual_memcpy.rs
clippy_lints/src/loops/mod.rs
clippy_lints/src/loops/mut_range_bound.rs
clippy_lints/src/loops/needless_collect.rs
clippy_lints/src/loops/needless_range_loop.rs
clippy_lints/src/loops/never_loop.rs
clippy_lints/src/loops/same_item_push.rs
clippy_lints/src/loops/single_element_loop.rs
clippy_lints/src/loops/utils.rs
clippy_lints/src/loops/while_immutable_condition.rs
clippy_lints/src/loops/while_let_loop.rs
clippy_lints/src/loops/while_let_on_iterator.rs
clippy_lints/src/macro_use.rs
clippy_lints/src/main_recursion.rs
clippy_lints/src/manual_async_fn.rs
clippy_lints/src/manual_map.rs
clippy_lints/src/manual_non_exhaustive.rs
clippy_lints/src/manual_ok_or.rs
clippy_lints/src/manual_strip.rs
clippy_lints/src/manual_unwrap_or.rs
clippy_lints/src/map_clone.rs
clippy_lints/src/map_err_ignore.rs
clippy_lints/src/map_identity.rs
clippy_lints/src/map_unit_fn.rs
clippy_lints/src/match_on_vec_items.rs
clippy_lints/src/matches.rs
clippy_lints/src/mem_discriminant.rs
clippy_lints/src/mem_forget.rs
clippy_lints/src/mem_replace.rs
clippy_lints/src/methods/bind_instead_of_map.rs
clippy_lints/src/methods/bytes_nth.rs
clippy_lints/src/methods/chars_cmp.rs [new file with mode: 0644]
clippy_lints/src/methods/chars_cmp_with_unwrap.rs [new file with mode: 0644]
clippy_lints/src/methods/chars_last_cmp.rs [new file with mode: 0644]
clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs [new file with mode: 0644]
clippy_lints/src/methods/chars_next_cmp.rs [new file with mode: 0644]
clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs [new file with mode: 0644]
clippy_lints/src/methods/clone_on_copy.rs
clippy_lints/src/methods/clone_on_ref_ptr.rs
clippy_lints/src/methods/expect_fun_call.rs
clippy_lints/src/methods/expect_used.rs
clippy_lints/src/methods/filetype_is_file.rs
clippy_lints/src/methods/filter_flat_map.rs
clippy_lints/src/methods/filter_map.rs
clippy_lints/src/methods/filter_map_flat_map.rs
clippy_lints/src/methods/filter_map_identity.rs
clippy_lints/src/methods/filter_map_map.rs
clippy_lints/src/methods/filter_map_next.rs
clippy_lints/src/methods/filter_next.rs
clippy_lints/src/methods/flat_map_identity.rs
clippy_lints/src/methods/from_iter_instead_of_collect.rs
clippy_lints/src/methods/get_unwrap.rs
clippy_lints/src/methods/implicit_clone.rs
clippy_lints/src/methods/inefficient_to_string.rs
clippy_lints/src/methods/inspect_for_each.rs
clippy_lints/src/methods/into_iter_on_ref.rs
clippy_lints/src/methods/iter_cloned_collect.rs
clippy_lints/src/methods/iter_count.rs
clippy_lints/src/methods/iter_next_slice.rs
clippy_lints/src/methods/iter_nth.rs
clippy_lints/src/methods/iter_nth_zero.rs
clippy_lints/src/methods/iter_skip_next.rs
clippy_lints/src/methods/iterator_step_by_zero.rs
clippy_lints/src/methods/manual_saturating_arithmetic.rs
clippy_lints/src/methods/map_collect_result_unit.rs
clippy_lints/src/methods/map_flatten.rs
clippy_lints/src/methods/map_unwrap_or.rs
clippy_lints/src/methods/mod.rs
clippy_lints/src/methods/ok_expect.rs
clippy_lints/src/methods/option_as_ref_deref.rs
clippy_lints/src/methods/option_map_or_none.rs
clippy_lints/src/methods/option_map_unwrap_or.rs
clippy_lints/src/methods/or_fun_call.rs
clippy_lints/src/methods/search_is_some.rs
clippy_lints/src/methods/single_char_add_str.rs [new file with mode: 0644]
clippy_lints/src/methods/single_char_insert_string.rs
clippy_lints/src/methods/single_char_pattern.rs
clippy_lints/src/methods/single_char_push_string.rs
clippy_lints/src/methods/skip_while_next.rs
clippy_lints/src/methods/string_extend_chars.rs
clippy_lints/src/methods/suspicious_map.rs
clippy_lints/src/methods/uninit_assumed_init.rs
clippy_lints/src/methods/unnecessary_filter_map.rs
clippy_lints/src/methods/unnecessary_fold.rs
clippy_lints/src/methods/unnecessary_lazy_eval.rs
clippy_lints/src/methods/unwrap_used.rs
clippy_lints/src/methods/useless_asref.rs
clippy_lints/src/methods/utils.rs [new file with mode: 0644]
clippy_lints/src/methods/wrong_self_convention.rs
clippy_lints/src/methods/zst_offset.rs
clippy_lints/src/minmax.rs
clippy_lints/src/misc.rs
clippy_lints/src/misc_early.rs
clippy_lints/src/missing_const_for_fn.rs
clippy_lints/src/missing_doc.rs
clippy_lints/src/missing_inline.rs
clippy_lints/src/modulo_arithmetic.rs
clippy_lints/src/multiple_crate_versions.rs
clippy_lints/src/mut_key.rs
clippy_lints/src/mut_mut.rs
clippy_lints/src/mut_mutex_lock.rs
clippy_lints/src/mut_reference.rs
clippy_lints/src/mutable_debug_assertion.rs
clippy_lints/src/mutex_atomic.rs
clippy_lints/src/needless_arbitrary_self_type.rs
clippy_lints/src/needless_bool.rs
clippy_lints/src/needless_borrow.rs
clippy_lints/src/needless_borrowed_ref.rs
clippy_lints/src/needless_continue.rs
clippy_lints/src/needless_pass_by_value.rs
clippy_lints/src/needless_question_mark.rs
clippy_lints/src/needless_update.rs
clippy_lints/src/neg_cmp_op_on_partial_ord.rs
clippy_lints/src/neg_multiply.rs
clippy_lints/src/new_without_default.rs
clippy_lints/src/no_effect.rs
clippy_lints/src/non_copy_const.rs
clippy_lints/src/non_expressive_names.rs
clippy_lints/src/open_options.rs
clippy_lints/src/option_env_unwrap.rs
clippy_lints/src/option_if_let_else.rs
clippy_lints/src/overflow_check_conditional.rs
clippy_lints/src/panic_in_result_fn.rs
clippy_lints/src/panic_unimplemented.rs
clippy_lints/src/partialeq_ne_impl.rs
clippy_lints/src/pass_by_ref_or_value.rs
clippy_lints/src/path_buf_push_overwrite.rs
clippy_lints/src/pattern_type_mismatch.rs
clippy_lints/src/precedence.rs
clippy_lints/src/ptr.rs
clippy_lints/src/ptr_eq.rs
clippy_lints/src/ptr_offset_with_cast.rs
clippy_lints/src/question_mark.rs
clippy_lints/src/ranges.rs
clippy_lints/src/redundant_clone.rs
clippy_lints/src/redundant_closure_call.rs
clippy_lints/src/redundant_else.rs
clippy_lints/src/redundant_field_names.rs
clippy_lints/src/redundant_pub_crate.rs
clippy_lints/src/redundant_slicing.rs
clippy_lints/src/redundant_static_lifetimes.rs
clippy_lints/src/ref_option_ref.rs
clippy_lints/src/reference.rs
clippy_lints/src/regex.rs
clippy_lints/src/repeat_once.rs
clippy_lints/src/returns.rs
clippy_lints/src/self_assignment.rs
clippy_lints/src/semicolon_if_nothing_returned.rs
clippy_lints/src/serde_api.rs
clippy_lints/src/shadow.rs
clippy_lints/src/single_component_path_imports.rs
clippy_lints/src/size_of_in_element_count.rs
clippy_lints/src/slow_vector_initialization.rs
clippy_lints/src/stable_sort_primitive.rs
clippy_lints/src/strings.rs
clippy_lints/src/suspicious_operation_groupings.rs
clippy_lints/src/suspicious_trait_impl.rs
clippy_lints/src/swap.rs
clippy_lints/src/tabs_in_doc_comments.rs
clippy_lints/src/temporary_assignment.rs
clippy_lints/src/to_digit_is_some.rs
clippy_lints/src/to_string_in_display.rs
clippy_lints/src/trait_bounds.rs
clippy_lints/src/transmute/crosspointer_transmute.rs
clippy_lints/src/transmute/mod.rs
clippy_lints/src/transmute/transmute_float_to_int.rs
clippy_lints/src/transmute/transmute_int_to_bool.rs
clippy_lints/src/transmute/transmute_int_to_char.rs
clippy_lints/src/transmute/transmute_int_to_float.rs
clippy_lints/src/transmute/transmute_ptr_to_ptr.rs
clippy_lints/src/transmute/transmute_ptr_to_ref.rs
clippy_lints/src/transmute/transmute_ref_to_ref.rs
clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
clippy_lints/src/transmute/unsound_collection_transmute.rs
clippy_lints/src/transmute/useless_transmute.rs
clippy_lints/src/transmute/utils.rs
clippy_lints/src/transmute/wrong_transmute.rs
clippy_lints/src/transmuting_null.rs
clippy_lints/src/try_err.rs
clippy_lints/src/types/borrowed_box.rs
clippy_lints/src/types/box_vec.rs
clippy_lints/src/types/linked_list.rs
clippy_lints/src/types/mod.rs
clippy_lints/src/types/option_option.rs
clippy_lints/src/types/rc_buffer.rs
clippy_lints/src/types/redundant_allocation.rs
clippy_lints/src/types/utils.rs
clippy_lints/src/types/vec_box.rs
clippy_lints/src/undropped_manually_drops.rs
clippy_lints/src/unicode.rs
clippy_lints/src/unit_return_expecting_ord.rs
clippy_lints/src/unit_types/let_unit_value.rs [new file with mode: 0644]
clippy_lints/src/unit_types/mod.rs [new file with mode: 0644]
clippy_lints/src/unit_types/unit_arg.rs [new file with mode: 0644]
clippy_lints/src/unit_types/unit_cmp.rs [new file with mode: 0644]
clippy_lints/src/unit_types/utils.rs [new file with mode: 0644]
clippy_lints/src/unnamed_address.rs
clippy_lints/src/unnecessary_sort_by.rs
clippy_lints/src/unnecessary_wraps.rs
clippy_lints/src/unnested_or_patterns.rs
clippy_lints/src/unsafe_removed_from_name.rs
clippy_lints/src/unused_io_amount.rs
clippy_lints/src/unused_self.rs
clippy_lints/src/unused_unit.rs
clippy_lints/src/unwrap.rs
clippy_lints/src/unwrap_in_result.rs
clippy_lints/src/upper_case_acronyms.rs
clippy_lints/src/use_self.rs
clippy_lints/src/useless_conversion.rs
clippy_lints/src/utils/author.rs
clippy_lints/src/utils/inspector.rs
clippy_lints/src/utils/internal_lints.rs
clippy_lints/src/utils/mod.rs
clippy_lints/src/vec.rs
clippy_lints/src/vec_init_then_push.rs
clippy_lints/src/vec_resize_to_zero.rs
clippy_lints/src/verbose_file_reads.rs
clippy_lints/src/wildcard_dependencies.rs
clippy_lints/src/wildcard_imports.rs
clippy_lints/src/write.rs
clippy_lints/src/zero_div_zero.rs
clippy_lints/src/zero_sized_map_values.rs
clippy_utils/Cargo.toml
clippy_utils/src/ast_utils.rs
clippy_utils/src/diagnostics.rs
clippy_utils/src/eager_or_lazy.rs
clippy_utils/src/hir_utils.rs
clippy_utils/src/lib.rs
clippy_utils/src/paths.rs
clippy_utils/src/ptr.rs
clippy_utils/src/source.rs [new file with mode: 0644]
clippy_utils/src/sugg.rs
clippy_utils/src/ty.rs [new file with mode: 0644]
clippy_utils/src/usage.rs
doc/basics.md
rust-toolchain
tests/ui-toml/upper_case_acronyms_aggressive/upper_case_acronyms.rs
tests/ui/def_id_nocore.stderr
tests/ui/dereference.fixed [deleted file]
tests/ui/dereference.rs [deleted file]
tests/ui/dereference.stderr [deleted file]
tests/ui/explicit_deref_methods.fixed [new file with mode: 0644]
tests/ui/explicit_deref_methods.rs [new file with mode: 0644]
tests/ui/explicit_deref_methods.stderr [new file with mode: 0644]
tests/ui/field_reassign_with_default.rs
tests/ui/field_reassign_with_default.stderr
tests/ui/if_then_some_else_none.rs [new file with mode: 0644]
tests/ui/if_then_some_else_none.stderr [new file with mode: 0644]
tests/ui/inconsistent_struct_constructor.stderr
tests/ui/manual_flatten.rs
tests/ui/manual_flatten.stderr
tests/ui/manual_map_option.fixed
tests/ui/manual_map_option.rs
tests/ui/manual_map_option.stderr
tests/ui/manual_unwrap_or.fixed
tests/ui/manual_unwrap_or.rs
tests/ui/match_single_binding2.fixed [new file with mode: 0644]
tests/ui/match_single_binding2.rs [new file with mode: 0644]
tests/ui/match_single_binding2.stderr [new file with mode: 0644]
tests/ui/match_wildcard_for_single_variants.fixed
tests/ui/match_wildcard_for_single_variants.rs
tests/ui/match_wildcard_for_single_variants.stderr
tests/ui/mem_replace.fixed
tests/ui/mem_replace.rs
tests/ui/mem_replace.stderr
tests/ui/needless_question_mark.fixed
tests/ui/needless_question_mark.rs
tests/ui/needless_question_mark.stderr
tests/ui/new_ret_no_self.rs
tests/ui/new_without_default.rs
tests/ui/new_without_default.stderr
tests/ui/or_fun_call.fixed
tests/ui/or_fun_call.rs
tests/ui/or_fun_call.stderr
tests/ui/print.stderr
tests/ui/print_literal.stderr
tests/ui/search_is_some.rs
tests/ui/search_is_some.stderr
tests/ui/search_is_some_fixable.fixed
tests/ui/search_is_some_fixable.rs
tests/ui/search_is_some_fixable.stderr
tests/ui/string_lit_as_bytes.fixed
tests/ui/string_lit_as_bytes.rs
tests/ui/string_lit_as_bytes.stderr
tests/ui/suspicious_map.rs
tests/ui/suspicious_map.stderr
tests/ui/unnecessary_cast.stderr
tests/ui/upper_case_acronyms.rs
tests/ui/use_self.fixed
tests/ui/use_self.rs
tests/ui/wildcard_enum_match_arm.fixed
tests/ui/wildcard_enum_match_arm.stderr
tests/ui/write_literal.stderr
tests/ui/write_literal_2.rs [new file with mode: 0644]
tests/ui/write_literal_2.stderr [new file with mode: 0644]
tests/ui/wrong_self_convention.rs
tests/ui/wrong_self_convention.stderr
tests/ui/wrong_self_conventions_mut.rs [new file with mode: 0644]
tests/ui/wrong_self_conventions_mut.stderr [new file with mode: 0644]

index 41c334c68169b98e3cb73305bdd2e5a844ad7fba..f7916511edfe7a7e662962ae971b68cd2a531af1 100644 (file)
@@ -2103,6 +2103,7 @@ Released 2018-09-13
 [`if_let_some_result`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_let_some_result
 [`if_not_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_not_else
 [`if_same_then_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_same_then_else
+[`if_then_some_else_none`]: https://rust-lang.github.io/rust-clippy/master/index.html#if_then_some_else_none
 [`ifs_same_cond`]: https://rust-lang.github.io/rust-clippy/master/index.html#ifs_same_cond
 [`implicit_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_clone
 [`implicit_hasher`]: https://rust-lang.github.io/rust-clippy/master/index.html#implicit_hasher
index 5954ab25d194234539ec255b21520d480b00cc23..e0a4d4455e9c6c2182ebb60639f228f35eaec621 100644 (file)
@@ -18,11 +18,13 @@ All contributors are expected to follow the [Rust Code of Conduct].
     - [Finding something to fix/improve](#finding-something-to-fiximprove)
   - [Writing code](#writing-code)
   - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work)
+    - [IntelliJ Rust](#intellij-rust)
+    - [Rust Analyzer](#rust-analyzer)
   - [How Clippy works](#how-clippy-works)
-  - [Syncing changes between Clippy and `rust-lang/rust`](#syncing-changes-between-clippy-and-rust-langrust)
+  - [Syncing changes between Clippy and [`rust-lang/rust`]](#syncing-changes-between-clippy-and-rust-langrust)
     - [Patching git-subtree to work with big repos](#patching-git-subtree-to-work-with-big-repos)
-    - [Performing the sync from `rust-lang/rust` to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
-    - [Performing the sync from Clippy to `rust-lang/rust`](#performing-the-sync-from-clippy-to-rust-langrust)
+    - [Performing the sync from [`rust-lang/rust`] to Clippy](#performing-the-sync-from-rust-langrust-to-clippy)
+    - [Performing the sync from Clippy to [`rust-lang/rust`]](#performing-the-sync-from-clippy-to-rust-langrust)
     - [Defining remotes](#defining-remotes)
   - [Issue and PR triage](#issue-and-pr-triage)
   - [Bors and Homu](#bors-and-homu)
@@ -105,21 +107,41 @@ quick read.
 
 ## Getting code-completion for rustc internals to work
 
-Unfortunately, [`rust-analyzer`][ra_homepage] does not (yet?) understand how Clippy uses compiler-internals
+### IntelliJ Rust
+Unfortunately, [`IntelliJ Rust`][IntelliJ_rust_homepage] does not (yet?) understand how Clippy uses compiler-internals
 using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not
 available via a `rustup` component at the time of writing.
 To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via
 `git clone https://github.com/rust-lang/rust/`.
 Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies
-which rust-analyzer will be able to understand.
-Run `cargo dev ra_setup --repo-path <repo-path>` where `<repo-path>` is an absolute path to the rustc repo
+which `IntelliJ Rust` will be able to understand.
+Run `cargo dev ide_setup --repo-path <repo-path>` where `<repo-path>` is a path to the rustc repo
 you just cloned.
 The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
 Clippys `Cargo.toml`s and should allow rust-analyzer to understand most of the types that Clippy uses.
 Just make sure to remove the dependencies again before finally making a pull request!
 
-[ra_homepage]: https://rust-analyzer.github.io/
 [rustc_repo]: https://github.com/rust-lang/rust/
+[IntelliJ_rust_homepage]: https://intellij-rust.github.io/
+
+### Rust Analyzer
+As of [#6869][6869], [`rust-analyzer`][ra_homepage] can understand that Clippy uses compiler-internals
+using `extern crate` when `package.metadata.rust-analyzer.rustc_private` is set to `true` in Clippys `Cargo.toml.`
+You will required a `nightly` toolchain with the `rustc-dev` component installed.
+Make sure that in the `rust-analyzer` configuration, you set
+```
+{ "rust-analyzer.rustcSource": "discover" }
+```
+and
+```
+{ "rust-analyzer.updates.channel": "nightly" }
+```
+You should be able to see information on things like `Expr` or `EarlyContext` now if you hover them, also
+a lot more type hints.
+This will work with `rust-analyzer 2021-03-15` shipped in nightly `1.52.0-nightly (107896c32 2021-03-15)` or later.
+
+[ra_homepage]: https://rust-analyzer.github.io/
+[6869]: https://github.com/rust-lang/rust-clippy/pull/6869
 
 ## How Clippy works
 
index 2b9488de28994582958504cc82fc5ce683ec527e..cade44a0a9ab4a8bc0cd4a3ad2489f86a4dded74 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.52"
+version = "0.1.53"
 authors = ["The Rust Clippy Developers"]
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
@@ -37,6 +37,8 @@ clippy-mini-macro-test = { version = "0.2", path = "mini-macro" }
 serde = { version = "1.0", features = ["derive"] }
 derive-new = "0.5"
 regex = "1.4"
+quote = "1"
+syn = { version = "1", features = ["full"] }
 
 # A noop dependency that changes in the Rust repository, it's a bit of a hack.
 # See the `src/tools/rustc-workspace-hack/README.md` file in `rust-lang/rust`
index a26d6aba10d20ed5beb03e9e49d038524e0c4387..111c79c332d0596e4a5d25cb338c95374da4a88d 100644 (file)
@@ -90,7 +90,7 @@ fn output_err(err: CliError) {
             },
             CliError::RaSetupActive => {
                 eprintln!(
-                    "error: a local rustc repo is enabled as path dependency via `cargo dev ra_setup`.
+                    "error: a local rustc repo is enabled as path dependency via `cargo dev ide_setup`.
 Not formatting because that would format the local repo as well!
 Please revert the changes to Cargo.tomls first."
                 );
diff --git a/clippy_dev/src/ide_setup.rs b/clippy_dev/src/ide_setup.rs
new file mode 100644 (file)
index 0000000..defb113
--- /dev/null
@@ -0,0 +1,103 @@
+use std::fs;
+use std::fs::File;
+use std::io::prelude::*;
+use std::path::{Path, PathBuf};
+
+// This module takes an absolute path to a rustc repo and alters the dependencies to point towards
+// the respective rustc subcrates instead of using extern crate xyz.
+// This allows rust analyzer to analyze rustc internals and show proper information inside clippy
+// code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details
+
+/// # Panics
+///
+/// Panics if `rustc_path` does not lead to a rustc repo or the files could not be read
+pub fn run(rustc_path: Option<&str>) {
+    // we can unwrap here because the arg is required by clap
+    let rustc_path = PathBuf::from(rustc_path.unwrap())
+        .canonicalize()
+        .expect("failed to get the absolute repo path");
+    assert!(rustc_path.is_dir(), "path is not a directory");
+    let rustc_source_basedir = rustc_path.join("compiler");
+    assert!(
+        rustc_source_basedir.is_dir(),
+        "are you sure the path leads to a rustc repo?"
+    );
+
+    let clippy_root_manifest = fs::read_to_string("Cargo.toml").expect("failed to read ./Cargo.toml");
+    let clippy_root_lib_rs = fs::read_to_string("src/driver.rs").expect("failed to read ./src/driver.rs");
+    inject_deps_into_manifest(
+        &rustc_source_basedir,
+        "Cargo.toml",
+        &clippy_root_manifest,
+        &clippy_root_lib_rs,
+    )
+    .expect("Failed to inject deps into ./Cargo.toml");
+
+    let clippy_lints_manifest =
+        fs::read_to_string("clippy_lints/Cargo.toml").expect("failed to read ./clippy_lints/Cargo.toml");
+    let clippy_lints_lib_rs =
+        fs::read_to_string("clippy_lints/src/lib.rs").expect("failed to read ./clippy_lints/src/lib.rs");
+    inject_deps_into_manifest(
+        &rustc_source_basedir,
+        "clippy_lints/Cargo.toml",
+        &clippy_lints_manifest,
+        &clippy_lints_lib_rs,
+    )
+    .expect("Failed to inject deps into ./clippy_lints/Cargo.toml");
+}
+
+fn inject_deps_into_manifest(
+    rustc_source_dir: &Path,
+    manifest_path: &str,
+    cargo_toml: &str,
+    lib_rs: &str,
+) -> std::io::Result<()> {
+    // do not inject deps if we have aleady done so
+    if cargo_toml.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") {
+        eprintln!(
+            "cargo dev ide_setup: warning: deps already found inside {}, doing nothing.",
+            manifest_path
+        );
+        return Ok(());
+    }
+
+    let extern_crates = lib_rs
+        .lines()
+        // get the deps
+        .filter(|line| line.starts_with("extern crate"))
+        // we have something like "extern crate foo;", we only care about the "foo"
+        //              â†“          â†“
+        // extern crate rustc_middle;
+        .map(|s| &s[13..(s.len() - 1)]);
+
+    let new_deps = extern_crates.map(|dep| {
+        // format the dependencies that are going to be put inside the Cargo.toml
+        format!(
+            "{dep} = {{ path = \"{source_path}/{dep}\" }}\n",
+            dep = dep,
+            source_path = rustc_source_dir.display()
+        )
+    });
+
+    // format a new [dependencies]-block with the new deps we need to inject
+    let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n");
+    new_deps.for_each(|dep_line| {
+        all_deps.push_str(&dep_line);
+    });
+    all_deps.push_str("\n[dependencies]\n");
+
+    // replace "[dependencies]" with
+    // [dependencies]
+    // dep1 = { path = ... }
+    // dep2 = { path = ... }
+    // etc
+    let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1);
+
+    // println!("{}", new_manifest);
+    let mut file = File::create(manifest_path)?;
+    file.write_all(new_manifest.as_bytes())?;
+
+    println!("Dependency paths injected: {}", manifest_path);
+
+    Ok(())
+}
index a95abfaceaae026af80e2d84dc628982d18d2ee6..a5e94683878ec7ff78b9a8eb932546fc273c1e7b 100644 (file)
@@ -12,8 +12,8 @@
 
 pub mod bless;
 pub mod fmt;
+pub mod ide_setup;
 pub mod new_lint;
-pub mod ra_setup;
 pub mod serve;
 pub mod stderr_length_check;
 pub mod update_lints;
index 2a9f3e5348c4156c7ac5e0c7e473595ccf35d6e1..f4da783502c5fdc8c8e51dd8eef0a4ee61929c67 100644 (file)
@@ -1,7 +1,7 @@
 #![cfg_attr(feature = "deny-warnings", deny(warnings))]
 
 use clap::{App, Arg, ArgMatches, SubCommand};
-use clippy_dev::{bless, fmt, new_lint, ra_setup, serve, stderr_length_check, update_lints};
+use clippy_dev::{bless, fmt, ide_setup, new_lint, serve, stderr_length_check, update_lints};
 fn main() {
     let matches = get_clap_config();
 
@@ -34,7 +34,7 @@ fn main() {
         ("limit_stderr_length", _) => {
             stderr_length_check::check();
         },
-        ("ra_setup", Some(matches)) => ra_setup::run(matches.value_of("rustc-repo-path")),
+        ("ide_setup", Some(matches)) => ide_setup::run(matches.value_of("rustc-repo-path")),
         ("serve", Some(matches)) => {
             let port = matches.value_of("port").unwrap().parse().unwrap();
             let lint = matches.value_of("lint");
@@ -138,8 +138,8 @@ fn get_clap_config<'a>() -> ArgMatches<'a> {
                 .about("Ensures that stderr files do not grow longer than a certain amount of lines."),
         )
         .subcommand(
-            SubCommand::with_name("ra_setup")
-                .about("Alter dependencies so rust-analyzer can find rustc internals")
+            SubCommand::with_name("ide_setup")
+                .about("Alter dependencies so Intellij Rust can find rustc internals")
                 .arg(
                     Arg::with_name("rustc-repo-path")
                         .long("repo-path")
diff --git a/clippy_dev/src/ra_setup.rs b/clippy_dev/src/ra_setup.rs
deleted file mode 100644 (file)
index d0e2193..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-use std::fs;
-use std::fs::File;
-use std::io::prelude::*;
-use std::path::{Path, PathBuf};
-
-// This module takes an absolute path to a rustc repo and alters the dependencies to point towards
-// the respective rustc subcrates instead of using extern crate xyz.
-// This allows rust analyzer to analyze rustc internals and show proper information inside clippy
-// code. See https://github.com/rust-analyzer/rust-analyzer/issues/3517 and https://github.com/rust-lang/rust-clippy/issues/5514 for details
-
-/// # Panics
-///
-/// Panics if `rustc_path` does not lead to a rustc repo or the files could not be read
-pub fn run(rustc_path: Option<&str>) {
-    // we can unwrap here because the arg is required by clap
-    let rustc_path = PathBuf::from(rustc_path.unwrap())
-        .canonicalize()
-        .expect("failed to get the absolute repo path");
-    assert!(rustc_path.is_dir(), "path is not a directory");
-    let rustc_source_basedir = rustc_path.join("compiler");
-    assert!(
-        rustc_source_basedir.is_dir(),
-        "are you sure the path leads to a rustc repo?"
-    );
-
-    let clippy_root_manifest = fs::read_to_string("Cargo.toml").expect("failed to read ./Cargo.toml");
-    let clippy_root_lib_rs = fs::read_to_string("src/driver.rs").expect("failed to read ./src/driver.rs");
-    inject_deps_into_manifest(
-        &rustc_source_basedir,
-        "Cargo.toml",
-        &clippy_root_manifest,
-        &clippy_root_lib_rs,
-    )
-    .expect("Failed to inject deps into ./Cargo.toml");
-
-    let clippy_lints_manifest =
-        fs::read_to_string("clippy_lints/Cargo.toml").expect("failed to read ./clippy_lints/Cargo.toml");
-    let clippy_lints_lib_rs =
-        fs::read_to_string("clippy_lints/src/lib.rs").expect("failed to read ./clippy_lints/src/lib.rs");
-    inject_deps_into_manifest(
-        &rustc_source_basedir,
-        "clippy_lints/Cargo.toml",
-        &clippy_lints_manifest,
-        &clippy_lints_lib_rs,
-    )
-    .expect("Failed to inject deps into ./clippy_lints/Cargo.toml");
-}
-
-fn inject_deps_into_manifest(
-    rustc_source_dir: &Path,
-    manifest_path: &str,
-    cargo_toml: &str,
-    lib_rs: &str,
-) -> std::io::Result<()> {
-    // do not inject deps if we have aleady done so
-    if cargo_toml.contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]") {
-        eprintln!(
-            "cargo dev ra_setup: warning: deps already found inside {}, doing nothing.",
-            manifest_path
-        );
-        return Ok(());
-    }
-
-    let extern_crates = lib_rs
-        .lines()
-        // get the deps
-        .filter(|line| line.starts_with("extern crate"))
-        // we have something like "extern crate foo;", we only care about the "foo"
-        //              â†“          â†“
-        // extern crate rustc_middle;
-        .map(|s| &s[13..(s.len() - 1)]);
-
-    let new_deps = extern_crates.map(|dep| {
-        // format the dependencies that are going to be put inside the Cargo.toml
-        format!(
-            "{dep} = {{ path = \"{source_path}/{dep}\" }}\n",
-            dep = dep,
-            source_path = rustc_source_dir.display()
-        )
-    });
-
-    // format a new [dependencies]-block with the new deps we need to inject
-    let mut all_deps = String::from("[target.'cfg(NOT_A_PLATFORM)'.dependencies]\n");
-    new_deps.for_each(|dep_line| {
-        all_deps.push_str(&dep_line);
-    });
-    all_deps.push_str("\n[dependencies]\n");
-
-    // replace "[dependencies]" with
-    // [dependencies]
-    // dep1 = { path = ... }
-    // dep2 = { path = ... }
-    // etc
-    let new_manifest = cargo_toml.replacen("[dependencies]\n", &all_deps, 1);
-
-    // println!("{}", new_manifest);
-    let mut file = File::create(manifest_path)?;
-    file.write_all(new_manifest.as_bytes())?;
-
-    println!("Dependency paths injected: {}", manifest_path);
-
-    Ok(())
-}
index 6bd6c079276e4bde141d0ae61f0a6bdc0612fda6..05cdd9d064a8e44b3d0d4d28361afe4b74ffc126 100644 (file)
@@ -1,7 +1,7 @@
 [package]
 name = "clippy_lints"
 # begin automatic update
-version = "0.1.52"
+version = "0.1.53"
 # end automatic update
 authors = ["The Rust Clippy Developers"]
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
@@ -20,7 +20,6 @@ pulldown-cmark = { version = "0.8", default-features = false }
 quine-mc_cluskey = "0.2.2"
 regex-syntax = "0.6"
 serde = { version = "1.0", features = ["derive"] }
-smallvec = { version = "1", features = ["union"] }
 toml = "0.5.3"
 unicode-normalization = "0.1"
 semver = "0.11"
@@ -28,8 +27,6 @@ rustc-semver = "1.1.0"
 # NOTE: cargo requires serde feat in its url dep
 # see <https://github.com/rust-lang/rust/pull/63587#issuecomment-522343864>
 url = { version = "2.1.0", features = ["serde"] }
-quote = "1"
-syn = { version = "1", features = ["full"] }
 
 [features]
 deny-warnings = []
index 1d511a86c9099f44c815300ad18920481ca50c1d..3d04abe094d7811e25c34e0d42b9b8456776bb39 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
 use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
index 61fdf9495b918c6b44f8ca72a8b2833a9a27e482..c560f545d6a6155fd6785bd715afbb75efbb5019 100644 (file)
@@ -1,5 +1,5 @@
 use crate::consts::constant_simple;
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
index c30d65bbc57043a34881b284cb92631316b28a85..4b31e16094e9f9a1b2d6b84bef58239698c62b8b 100644 (file)
@@ -1,10 +1,9 @@
+use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use crate::utils::span_lint_and_help;
-
 declare_clippy_lint! {
     /// **What it does:** Checks for usage of `as` conversions.
     ///
index ef1f1a14afcace4c1c21eb7e1e3e373b02194522..b970c71b753cec24a83c6cc20f65918cab12d0e0 100644 (file)
@@ -1,6 +1,6 @@
 use std::fmt;
 
-use crate::utils::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions};
 use rustc_lint::{EarlyContext, EarlyLintPass, Lint};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
index 77b26faaa586a35ce74bf5c7757f61453a26feb2..16905781c56b6851b5ffcce9c89b1ccbdc381f0b 100644 (file)
@@ -1,5 +1,7 @@
 use crate::consts::{constant, Constant};
-use crate::utils::{is_direct_expn_of, is_expn_of, match_panic_call, snippet_opt, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call};
 use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
index e13f62d04281ac3cbfc5602a04ef4e3db3b0d5e1..bc6eec0051a41d872c466b2a0a4f5becee8b5585 100644 (file)
@@ -1,7 +1,8 @@
-use crate::utils::{
-    eq_expr_value, get_trait_def_id, implements_trait, snippet_opt, span_lint_and_then, trait_ref_of_method,
-};
-use crate::utils::{higher, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{eq_expr_value, get_trait_def_id, trait_ref_of_method};
+use clippy_utils::{higher, paths, sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -92,7 +93,7 @@ macro_rules! ops {
                              $($trait_name:ident),+) => {
                                 match $op {
                                     $(hir::BinOpKind::$trait_name => {
-                                        let [krate, module] = crate::utils::paths::OPS_MODULE;
+                                        let [krate, module] = paths::OPS_MODULE;
                                         let path: [&str; 3] = [krate, module, concat!(stringify!($trait_name), "Assign")];
                                         let trait_id = if let Some(trait_id) = get_trait_def_id($cx, &path) {
                                             trait_id
index 869a5c28d051126d10b4abcc84da3382f90447cc..e6c7c68f91a07a0ce3cb320f67a43aa7a7a5d0e8 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{implements_trait, snippet, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::implements_trait;
 use rustc_errors::Applicability;
 use rustc_hir::{AsyncGeneratorKind, Body, BodyId, ExprKind, GeneratorKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
index 703d8a6f62bb1f07ab305dfec36e32097d10f070..dfb1819932563c03575cae032477a045f03e01f9 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{match_def_path, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::match_def_path;
 use if_chain::if_chain;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{Expr, ExprKind};
index 6250810bc42773179869a8d5e9fe4a95e840546c..3cef8d2a78bf727eb6e99e0414b29ca1f216ac48 100644 (file)
@@ -1,9 +1,8 @@
 //! checks for attributes
 
-use crate::utils::{
-    first_line_of_span, is_present_in_source, match_panic_def_id, snippet_opt, span_lint, span_lint_and_help,
-    span_lint_and_sugg, span_lint_and_then, without_block_comments,
-};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::match_panic_def_id;
+use clippy_utils::source::{first_line_of_span, is_present_in_source, snippet_opt, without_block_comments};
 use if_chain::if_chain;
 use rustc_ast::{AttrKind, AttrStyle, Attribute, Lit, LitKind, MetaItemKind, NestedMetaItem};
 use rustc_errors::Applicability;
@@ -431,7 +430,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_
         |stmt| match &stmt.kind {
             StmtKind::Local(_) => true,
             StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
-            _ => false,
+            StmtKind::Item(_) => false,
         },
     )
 }
@@ -565,7 +564,7 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute) {
         // check for `rustfmt_skip` and `rustfmt::skip`
         if let Some(skip_item) = &items[1].meta_item();
         if skip_item.has_name(sym!(rustfmt_skip)) ||
-            skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym!(skip);
+            skip_item.path.segments.last().expect("empty path in attribute").ident.name == sym::skip;
         // Only lint outer attributes, because custom inner attributes are unstable
         // Tracking issue: https://github.com/rust-lang/rust/issues/54726
         if let AttrStyle::Outer = attr.style;
@@ -614,7 +613,7 @@ fn find_mismatched_target_os(items: &[NestedMetaItem]) -> Vec<(&str, Span)> {
                             }
                         }
                     },
-                    _ => {},
+                    MetaItemKind::NameValue(..) => {},
                 }
             }
         }
index 14b6a156c621e9963dc24a5abfe8d2a5237d06e1..68eee0520b38357b4a39bfe85e9069ef768ddf9c 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{match_def_path, paths, span_lint_and_note};
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::{match_def_path, paths};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{AsyncGeneratorKind, Body, BodyId, GeneratorKind};
 use rustc_lint::{LateContext, LateLintPass};
index a4ee54076ee98fc74b41f5fe67aa7d21e5dfcfed..f7daf3dab49481848e7d8e1fa3b0a2280bea109b 100644 (file)
@@ -1,6 +1,6 @@
 use crate::consts::{constant, Constant};
-use crate::utils::sugg::Sugg;
-use crate::utils::{span_lint, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
+use clippy_utils::sugg::Sugg;
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
index 153870fb416583472fe53ea582b8750641829534..b26ef33e056981ccd084623f97c13356dadec84b 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::{Pat, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
index b53f80fd8bc15801ea0238153a71444e4a657eab..badcf8d2a43cd8e7082ce27dd4ec4f39c852a70b 100644 (file)
@@ -1,7 +1,7 @@
-use crate::utils::{
-    differing_macro_contexts, get_parent_expr, get_trait_def_id, implements_trait, paths,
-    snippet_block_with_applicability, span_lint, span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::source::snippet_block_with_applicability;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{differing_macro_contexts, get_parent_expr};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
@@ -10,6 +10,7 @@
 use rustc_middle::hir::map::Map;
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
 
 declare_clippy_lint! {
     /// **What it does:** Checks for `if` conditions that use blocks containing an
@@ -61,7 +62,7 @@ fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
                 if let Some(parent) = get_parent_expr(self.cx, expr);
                 if let ExprKind::MethodCall(_, _, args, _) = parent.kind;
                 let caller = self.cx.typeck_results().expr_ty(&args[0]);
-                if let Some(iter_id) = get_trait_def_id(self.cx, &paths::ITERATOR);
+                if let Some(iter_id) = self.cx.tcx.get_diagnostic_item(sym::Iterator);
                 if implements_trait(self.cx, caller, iter_id, &[]);
                 then {
                     return;
index 0713303ec4b672c0190d8a8f2031060b0dceefb5..58d9aa9c005c2226966d0f148413a83f9b5ade15 100644 (file)
@@ -1,7 +1,7 @@
-use crate::utils::{
-    eq_expr_value, get_trait_def_id, implements_trait, in_macro, is_type_diagnostic_item, paths, snippet_opt,
-    span_lint_and_sugg, span_lint_and_then,
-};
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::{eq_expr_value, get_trait_def_id, in_macro, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
index eb5dc7ceecdc75908d133c98fe1e5603159a7002..846ac08e93a414deec23082898f1c9ddd74e984c 100644 (file)
@@ -1,6 +1,7 @@
-use crate::utils::{
-    contains_name, get_pat_name, match_type, paths, single_segment_path, snippet_with_applicability, span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::match_type;
+use clippy_utils::{contains_name, get_pat_name, paths, single_segment_path};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, UnOp};
index cc2869ab495c8ab9245d66d9503ffdbc0f44a94e..fce5c559672371cc119b8ec1e6dea5a97c299b57 100644 (file)
@@ -2,7 +2,8 @@
 
 use std::path::PathBuf;
 
-use crate::utils::{run_lints, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::run_lints;
 use rustc_hir::{hir_id::CRATE_HIR_ID, Crate};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
index b15fe65352ab1205ff4f517c3f1437ad5d107cd2..c9ef379be565d382a168474b1d8f72cf9cfbab0f 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_help;
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_hir::{Expr, ExprKind, PathSegment};
index 478832a5164a08e61920e8cb314acb826e637c0c..869deecfbd53a7ff882cb96d70f5dc2afa89ad00 100644 (file)
@@ -1,10 +1,12 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_constant;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::is_isize_or_usize;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, FloatTy, Ty};
 
-use crate::utils::{in_constant, is_isize_or_usize, snippet_opt, span_lint_and_sugg};
-
 use super::{utils, CAST_LOSSLESS};
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
index 33b06b8fe7caff7a12f1b50fa5b093e73a47d0c8..833ad122e0d4e20c46ae07f32c94157a4120f218 100644 (file)
@@ -1,9 +1,9 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::is_isize_or_usize;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, FloatTy, Ty};
 
-use crate::utils::{is_isize_or_usize, span_lint};
-
 use super::{utils, CAST_POSSIBLE_TRUNCATION};
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
index 56d301ed3e1c55485968c23b4fc5b5459fd6a6a7..2c5c1d7cb4654720e7c905ec3fd71d0d52611dbe 100644 (file)
@@ -1,9 +1,9 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::is_isize_or_usize;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::Ty;
 
-use crate::utils::{is_isize_or_usize, span_lint};
-
 use super::{utils, CAST_POSSIBLE_WRAP};
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
index a1c3900ce1f6c0a8a27d2ec6566e39140bde6b37..63ac8fd2dd269959f08149e2bf7c9efb36ef2efe 100644 (file)
@@ -1,9 +1,9 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::is_isize_or_usize;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, FloatTy, Ty};
 
-use crate::utils::{is_isize_or_usize, span_lint};
-
 use super::{utils, CAST_PRECISION_LOSS};
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
index 87fb5557be066abf082b02f672dec71963d7d668..5208156ffd2f40b8bf5dbe7bacebb92e48d2e497 100644 (file)
@@ -1,13 +1,12 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_hir_ty_cfg_dependant;
+use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind, GenericArg};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
 use rustc_span::symbol::sym;
 use rustc_target::abi::LayoutOf;
 
-use if_chain::if_chain;
-
-use crate::utils::{is_hir_ty_cfg_dependant, span_lint};
-
 use super::CAST_PTR_ALIGNMENT;
 
 pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
index 3fdc1c6168ba9b05d75080a2045effdf1e6aa56a..d9bf1ea58b97bb8a49ad3ed0588b0ec3c0175146 100644 (file)
@@ -1,11 +1,9 @@
+use clippy_utils::diagnostics::span_lint;
+use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind, MutTy, Mutability, TyKind, UnOp};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 
-use if_chain::if_chain;
-
-use crate::utils::span_lint;
-
 use super::CAST_REF_TO_MUT;
 
 pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
index 9656fbebd772089ab159054227c47e04c049939c..bf722d0a3f49689dad1edb04c7604fe4b7bc9cd2 100644 (file)
@@ -1,12 +1,11 @@
+use crate::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{method_chain_args, sext};
+use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
 
-use if_chain::if_chain;
-
-use crate::consts::{constant, Constant};
-use crate::utils::{method_chain_args, sext, span_lint};
-
 use super::CAST_SIGN_LOSS;
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
index ccaad1b8f2ac7eae4b2b6fad0a613cfe38bf1588..099a0de881ff0fb27eb868631b3742304bd424c0 100644 (file)
@@ -1,13 +1,12 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_with_applicability;
+use if_chain::if_chain;
 use rustc_ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, UintTy};
 
-use if_chain::if_chain;
-
-use crate::utils::{snippet_with_applicability, span_lint_and_then};
-
 use super::CHAR_LIT_AS_U8;
 
 pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
index a8d508585b5d416c3427d517bda9500ce9b41c89..35350d8a25b86ce9cefff7c6ce83e6a6b4ee9fe9 100644 (file)
@@ -1,10 +1,10 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty, UintTy};
 
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
-
 use super::{utils, FN_TO_NUMERIC_CAST};
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
index 0085c7b27b2906021fdccc7e706067207fae7c20..6287f479b5bfec4db1b51a6c1cc6fbe6310e97a0 100644 (file)
@@ -1,10 +1,10 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
 
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
-
 use super::{utils, FN_TO_NUMERIC_CAST_WITH_TRUNCATION};
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
index b726bd75f1d83ca7702a8d0aec672003d789ef33..d9e172c01a70d9342efcf8d903cbbe79b69c9712 100644 (file)
 mod unnecessary_cast;
 mod utils;
 
+use clippy_utils::is_hir_ty_cfg_dependant;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
-use crate::utils::is_hir_ty_cfg_dependant;
-
 declare_clippy_lint! {
     /// **What it does:** Checks for casts from any numerical to a float type where
     /// the receiving type cannot store all values from the original type without
index abfbadf3642bed7e875f67ff6e1197c3b030a4fc..9113e5a0920a250b57c849c6e13b9fac06c45114 100644 (file)
@@ -1,16 +1,15 @@
 use std::borrow::Cow;
 
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::meets_msrv;
+use clippy_utils::sugg::Sugg;
+use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Mutability, TyKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, TypeAndMut};
 use rustc_semver::RustcVersion;
 
-use if_chain::if_chain;
-
-use crate::utils::sugg::Sugg;
-use crate::utils::{meets_msrv, span_lint_and_sugg};
-
 use super::PTR_AS_PTR;
 
 const PTR_AS_PTR_MSRV: RustcVersion = RustcVersion::new(1, 38, 0);
index fa2a07ef1da0c098adb6374eec6594ee46f4a916..9ed359922fd4da4f4e80fa34dd9f99e72074e38a 100644 (file)
@@ -1,3 +1,7 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::numeric_literal::NumericLiteral;
+use clippy_utils::source::snippet_opt;
+use if_chain::if_chain;
 use rustc_ast::{LitFloatType, LitIntType, LitKind};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Lit, UnOp};
@@ -5,10 +9,6 @@
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, FloatTy, InferTy, Ty};
 
-use if_chain::if_chain;
-
-use crate::utils::{numeric_literal::NumericLiteral, snippet_opt, span_lint, span_lint_and_sugg};
-
 use super::UNNECESSARY_CAST;
 
 pub(super) fn check(
@@ -44,9 +44,18 @@ pub(super) fn check(
                 lint_unnecessary_cast(cx, expr, &literal_str, cast_from, cast_to);
             },
             LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed) => {},
+            LitKind::Int(_, LitIntType::Signed(_) | LitIntType::Unsigned(_))
+            | LitKind::Float(_, LitFloatType::Suffixed(_))
+                if cast_from.kind() == cast_to.kind() =>
+            {
+                if let Some(src) = snippet_opt(cx, lit.span) {
+                    let num_lit = NumericLiteral::from_lit_kind(&src, &lit.node).unwrap();
+                    lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
+                }
+            },
             _ => {
                 if cast_from.kind() == cast_to.kind() && !in_external_macro(cx.sess(), expr.span) {
-                    span_lint(
+                    span_lint_and_sugg(
                         cx,
                         UNNECESSARY_CAST,
                         expr.span,
@@ -54,6 +63,9 @@ pub(super) fn check(
                             "casting to the same type is unnecessary (`{}` -> `{}`)",
                             cast_from, cast_to
                         ),
+                        "try",
+                        literal_str,
+                        Applicability::MachineApplicable,
                     );
                     return true;
                 }
index 54bc69e058bc79460966385bec88e8cfce71f6bb..ed46cac493aad38c8ad686a4c254272c33844df3 100644 (file)
@@ -1,5 +1,8 @@
 //! lint on manually implemented checked conversions that could be transformed into `try_from`
 
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{meets_msrv, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -9,8 +12,6 @@
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
-use crate::utils::{meets_msrv, snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
-
 const CHECKED_CONVERSIONS_MSRV: RustcVersion = RustcVersion::new(1, 34, 0);
 
 declare_clippy_lint! {
index 658d445dfec54d0ad2e2a35bff964e571144bc84..4cc542f723ccea49daa1956bede86960f54ebd42 100644 (file)
@@ -1,5 +1,9 @@
 //! calculate cognitive complexity and warn about overly complex functions
 
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::LimitStack;
 use rustc_ast::ast::Attribute;
 use rustc_hir::intravisit::{walk_expr, FnKind, NestedVisitorMap, Visitor};
 use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId};
@@ -9,8 +13,6 @@
 use rustc_span::source_map::Span;
 use rustc_span::{sym, BytePos};
 
-use crate::utils::{is_type_diagnostic_item, snippet_opt, span_lint_and_help, LimitStack};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for methods with high cognitive complexity.
     ///
index 34f0e6ab027053840dbc0181063e492bc58e58d3..dae5c86bd4437b28c9025b399214351b15a99d8e 100644 (file)
 //!
 //! This lint is **warn** by default
 
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::{snippet_block, snippet_block_with_applicability};
+use clippy_utils::sugg::Sugg;
 use if_chain::if_chain;
 use rustc_ast::ast;
+use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use crate::utils::sugg::Sugg;
-use crate::utils::{snippet_block, snippet_block_with_applicability, span_lint_and_sugg, span_lint_and_then};
-use rustc_errors::Applicability;
-
 declare_clippy_lint! {
     /// **What it does:** Checks for nested `if` statements which can be collapsed
     /// by `&&`-combining their conditions.
index 3c45525684be455365a85c323d076ffbe44d92e3..e2b3686ddf0acfe3f22a47911eb6718a11232f86 100644 (file)
@@ -1,5 +1,6 @@
-use crate::utils::visitors::LocalUsedVisitor;
-use crate::utils::{path_to_local, span_lint_and_then, SpanlessEq};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::visitors::LocalUsedVisitor;
+use clippy_utils::{path_to_local, SpanlessEq};
 use if_chain::if_chain;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, QPath, StmtKind, UnOp};
index e309db25995fbfa0f8bf5cbe716765047442e269..d601cb7c22473ca561c059be8d29e1fda99e8735 100644 (file)
@@ -1,6 +1,6 @@
-use crate::utils::{
-    get_trait_def_id, if_sequence, implements_trait, parent_node_is_if_expr, paths, span_lint_and_help, SpanlessEq,
-};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{get_trait_def_id, if_sequence, parent_node_is_if_expr, paths, SpanlessEq};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
index 944aaafb46de59e5748f60d3f1773a8e71f87657..46093a16571bb884a0ba827450ab9626d8233067 100644 (file)
@@ -1,5 +1,6 @@
-use crate::utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash};
-use crate::utils::{get_parent_expr, if_sequence, span_lint_and_note};
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::{eq_expr_value, in_macro, search_same, SpanlessEq, SpanlessHash};
+use clippy_utils::{get_parent_expr, if_sequence};
 use rustc_hir::{Block, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
index 004bce5f62a8f74e4e850a1ecd0ca2d0b51a4f23..35079c6bedc02d10b47311a9737d1087c73bf5c4 100644 (file)
@@ -1,7 +1,11 @@
-use crate::utils::{is_copy, match_path, paths, span_lint_and_note};
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::ty::is_copy;
 use rustc_hir::{Impl, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::sym;
+
+use if_chain::if_chain;
 
 declare_clippy_lint! {
     /// **What it does:** Checks for types that implement `Copy` as well as
 
 impl<'tcx> LateLintPass<'tcx> for CopyIterator {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if let ItemKind::Impl(Impl {
-            of_trait: Some(ref trait_ref),
-            ..
-        }) = item.kind
-        {
+        if_chain! {
+            if let ItemKind::Impl(Impl {
+                of_trait: Some(ref trait_ref),
+                ..
+            }) = item.kind;
             let ty = cx.tcx.type_of(item.def_id);
-
-            if is_copy(cx, ty) && match_path(&trait_ref.path, &paths::ITERATOR) {
+            if is_copy(cx, ty);
+            if let Some(trait_id) = trait_ref.trait_def_id();
+            if cx.tcx.is_diagnostic_item(sym::Iterator, trait_id);
+            then {
                 span_lint_and_note(
                     cx,
                     COPY_ITERATOR,
index 200b6a565cc43e53e29184ae7da416bd79440213..ac890c90c97c38bdd22c01c6cedcf10ddf7641ce 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{match_def_path, paths, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
index e513dcce64e5349fb6668ceb1bf3fc864d577809..286cc7e223efb997cd3422b00a26a1382e3ac601 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{snippet_opt, span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::source::snippet_opt;
 use rustc_ast::ast;
 use rustc_ast::tokenstream::TokenStream;
 use rustc_errors::Applicability;
index 6fa1378b8c73d66971f75e93e899136a1aa93ab4..568a174445c1805a9669acbafb6c307580dcbd3c 100644 (file)
@@ -1,7 +1,6 @@
-use crate::utils::{
-    any_parent_is_automatically_derived, contains_name, match_def_path, paths, snippet_with_macro_callsite,
-};
-use crate::utils::{span_lint_and_note, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
+use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::{any_parent_is_automatically_derived, contains_name, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -105,6 +104,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         }
     }
 
+    #[allow(clippy::too_many_lines)]
     fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
         // start from the `let mut _ = _::default();` and look at all the following
         // statements, see if they re-assign the fields of the binding
@@ -198,6 +198,24 @@ fn check_block<'tcx>(&mut self, cx: &LateContext<'tcx>, block: &Block<'tcx>) {
                     .collect::<Vec<String>>()
                     .join(", ");
 
+                // give correct suggestion if generics are involved (see #6944)
+                let binding_type = if_chain! {
+                    if let ty::Adt(adt_def, substs) = binding_type.kind();
+                    if !substs.is_empty();
+                    let adt_def_ty_name = cx.tcx.item_name(adt_def.did);
+                    let generic_args = substs.iter().collect::<Vec<_>>();
+                    let tys_str = generic_args
+                        .iter()
+                        .map(ToString::to_string)
+                        .collect::<Vec<_>>()
+                        .join(", ");
+                    then {
+                        format!("{}::<{}>", adt_def_ty_name, &tys_str)
+                    } else {
+                        binding_type.to_string()
+                    }
+                };
+
                 let sugg = if ext_with_default {
                     if field_list.is_empty() {
                         format!("{}::default()", binding_type)
index 369efacc9bcf47f87aea5360875ba04f40cae2b4..d136db9373c2d1273fd1cf3b4f532a62b88006f9 100644 (file)
@@ -1,3 +1,6 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use if_chain::if_chain;
 use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
 use rustc_errors::Applicability;
 use rustc_hir::{
 };
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use if_chain::if_chain;
-
-use crate::utils::{snippet, span_lint_and_sugg};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for usage of unconstrained numeric literals which may cause default numeric fallback in type
     /// inference.
index b5fb51af1c7f31f6192ac279d107f274cf5156bc..1415f8e235a7e8936ef370fc3613d67a4c05e2a1 100644 (file)
@@ -1,11 +1,14 @@
-use crate::utils::{get_parent_expr, implements_trait, snippet, span_lint_and_sugg};
-use if_chain::if_chain;
-use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX, PREC_PREFIX};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::ty::peel_mid_ty_refs;
+use clippy_utils::{get_parent_node, in_macro, is_allowed};
+use rustc_ast::util::parser::PREC_PREFIX;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
+use rustc_middle::ty::{self, Ty, TyCtxt, TyS, TypeckResults};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::{symbol::sym, Span};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for explicit `deref()` or `deref_mut()` method calls.
     "Explicit use of deref or deref_mut method while not in a method chain."
 }
 
-declare_lint_pass!(Dereferencing => [
-    EXPLICIT_DEREF_METHODS
+impl_lint_pass!(Dereferencing => [
+    EXPLICIT_DEREF_METHODS,
 ]);
 
+#[derive(Default)]
+pub struct Dereferencing {
+    state: Option<(State, StateData)>,
+
+    // While parsing a `deref` method call in ufcs form, the path to the function is itself an
+    // expression. This is to store the id of that expression so it can be skipped when
+    // `check_expr` is called for it.
+    skip_expr: Option<HirId>,
+}
+
+struct StateData {
+    /// Span of the top level expression
+    span: Span,
+    /// The required mutability
+    target_mut: Mutability,
+}
+
+enum State {
+    // Any number of deref method calls.
+    DerefMethod {
+        // The number of calls in a sequence which changed the referenced type
+        ty_changed_count: usize,
+        is_final_ufcs: bool,
+    },
+}
+
+// A reference operation considered by this lint pass
+enum RefOp {
+    Method(Mutability),
+    Deref,
+    AddrOf,
+}
+
 impl<'tcx> LateLintPass<'tcx> for Dereferencing {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            if !expr.span.from_expansion();
-            if let ExprKind::MethodCall(ref method_name, _, ref args, _) = &expr.kind;
-            if args.len() == 1;
-
-            then {
-                if let Some(parent_expr) = get_parent_expr(cx, expr) {
-                    // Check if we have the whole call chain here
-                    if let ExprKind::MethodCall(..) = parent_expr.kind {
-                        return;
-                    }
-                    // Check for Expr that we don't want to be linted
-                    let precedence = parent_expr.precedence();
-                    match precedence {
-                        // Lint a Call is ok though
-                        ExprPrecedence::Call | ExprPrecedence::AddrOf => (),
-                        _ => {
-                            if precedence.order() >= PREC_PREFIX && precedence.order() <= PREC_POSTFIX {
-                                return;
-                            }
-                        }
+        // Skip path expressions from deref calls. e.g. `Deref::deref(e)`
+        if Some(expr.hir_id) == self.skip_expr.take() {
+            return;
+        }
+
+        // Stop processing sub expressions when a macro call is seen
+        if in_macro(expr.span) {
+            if let Some((state, data)) = self.state.take() {
+                report(cx, expr, state, data);
+            }
+            return;
+        }
+
+        let typeck = cx.typeck_results();
+        let (kind, sub_expr) = if let Some(x) = try_parse_ref_op(cx.tcx, typeck, expr) {
+            x
+        } else {
+            // The whole chain of reference operations has been seen
+            if let Some((state, data)) = self.state.take() {
+                report(cx, expr, state, data);
+            }
+            return;
+        };
+
+        match (self.state.take(), kind) {
+            (None, kind) => {
+                let parent = get_parent_node(cx.tcx, expr.hir_id);
+                let expr_ty = typeck.expr_ty(expr);
+
+                match kind {
+                    RefOp::Method(target_mut)
+                        if !is_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
+                            && is_linted_explicit_deref_position(parent, expr.hir_id, expr.span) =>
+                    {
+                        self.state = Some((
+                            State::DerefMethod {
+                                ty_changed_count: if deref_method_same_type(expr_ty, typeck.expr_ty(sub_expr)) {
+                                    0
+                                } else {
+                                    1
+                                },
+                                is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
+                            },
+                            StateData {
+                                span: expr.span,
+                                target_mut,
+                            },
+                        ));
                     }
+                    _ => (),
                 }
-                let name = method_name.ident.as_str();
-                lint_deref(cx, &*name, &args[0], args[0].span, expr.span);
-            }
+            },
+            (Some((State::DerefMethod { ty_changed_count, .. }, data)), RefOp::Method(_)) => {
+                self.state = Some((
+                    State::DerefMethod {
+                        ty_changed_count: if deref_method_same_type(typeck.expr_ty(expr), typeck.expr_ty(sub_expr)) {
+                            ty_changed_count
+                        } else {
+                            ty_changed_count + 1
+                        },
+                        is_final_ufcs: matches!(expr.kind, ExprKind::Call(..)),
+                    },
+                    data,
+                ));
+            },
+
+            (Some((state, data)), _) => report(cx, expr, state, data),
         }
     }
 }
 
-fn lint_deref(cx: &LateContext<'_>, method_name: &str, call_expr: &Expr<'_>, var_span: Span, expr_span: Span) {
-    match method_name {
-        "deref" => {
-            let impls_deref_trait = cx.tcx.lang_items().deref_trait().map_or(false, |id| {
-                implements_trait(cx, cx.typeck_results().expr_ty(&call_expr), id, &[])
-            });
-            if impls_deref_trait {
-                span_lint_and_sugg(
-                    cx,
-                    EXPLICIT_DEREF_METHODS,
-                    expr_span,
-                    "explicit deref method call",
-                    "try this",
-                    format!("&*{}", &snippet(cx, var_span, "..")),
-                    Applicability::MachineApplicable,
-                );
-            }
+fn try_parse_ref_op(
+    tcx: TyCtxt<'tcx>,
+    typeck: &'tcx TypeckResults<'_>,
+    expr: &'tcx Expr<'_>,
+) -> Option<(RefOp, &'tcx Expr<'tcx>)> {
+    let (def_id, arg) = match expr.kind {
+        ExprKind::MethodCall(_, _, [arg], _) => (typeck.type_dependent_def_id(expr.hir_id)?, arg),
+        ExprKind::Call(
+            Expr {
+                kind: ExprKind::Path(path),
+                hir_id,
+                ..
+            },
+            [arg],
+        ) => (typeck.qpath_res(path, *hir_id).opt_def_id()?, arg),
+        ExprKind::Unary(UnOp::Deref, sub_expr) if !typeck.expr_ty(sub_expr).is_unsafe_ptr() => {
+            return Some((RefOp::Deref, sub_expr));
         },
-        "deref_mut" => {
-            let impls_deref_mut_trait = cx.tcx.lang_items().deref_mut_trait().map_or(false, |id| {
-                implements_trait(cx, cx.typeck_results().expr_ty(&call_expr), id, &[])
-            });
-            if impls_deref_mut_trait {
-                span_lint_and_sugg(
-                    cx,
-                    EXPLICIT_DEREF_METHODS,
-                    expr_span,
-                    "explicit deref_mut method call",
-                    "try this",
-                    format!("&mut *{}", &snippet(cx, var_span, "..")),
-                    Applicability::MachineApplicable,
-                );
-            }
+        ExprKind::AddrOf(BorrowKind::Ref, _, sub_expr) => return Some((RefOp::AddrOf, sub_expr)),
+        _ => return None,
+    };
+    if tcx.is_diagnostic_item(sym::deref_method, def_id) {
+        Some((RefOp::Method(Mutability::Not), arg))
+    } else if tcx.trait_of_item(def_id)? == tcx.lang_items().deref_mut_trait()? {
+        Some((RefOp::Method(Mutability::Mut), arg))
+    } else {
+        None
+    }
+}
+
+// Checks whether the type for a deref call actually changed the type, not just the mutability of
+// the reference.
+fn deref_method_same_type(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
+    match (result_ty.kind(), arg_ty.kind()) {
+        (ty::Ref(_, result_ty, _), ty::Ref(_, arg_ty, _)) => TyS::same_type(result_ty, arg_ty),
+
+        // The result type for a deref method is always a reference
+        // Not matching the previous pattern means the argument type is not a reference
+        // This means that the type did change
+        _ => false,
+    }
+}
+
+// Checks whether the parent node is a suitable context for switching from a deref method to the
+// deref operator.
+fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId, child_span: Span) -> bool {
+    let parent = match parent {
+        Some(Node::Expr(e)) if e.span.ctxt() == child_span.ctxt() => e,
+        _ => return true,
+    };
+    match parent.kind {
+        // Leave deref calls in the middle of a method chain.
+        // e.g. x.deref().foo()
+        ExprKind::MethodCall(_, _, [self_arg, ..], _) if self_arg.hir_id == child_id => false,
+
+        // Leave deref calls resulting in a called function
+        // e.g. (x.deref())()
+        ExprKind::Call(func_expr, _) if func_expr.hir_id == child_id => false,
+
+        // Makes an ugly suggestion
+        // e.g. *x.deref() => *&*x
+        ExprKind::Unary(UnOp::Deref, _)
+        // Postfix expressions would require parens
+        | ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
+        | ExprKind::Field(..)
+        | ExprKind::Index(..)
+        | ExprKind::Err => false,
+
+        ExprKind::Box(..)
+        | ExprKind::ConstBlock(..)
+        | ExprKind::Array(_)
+        | ExprKind::Call(..)
+        | ExprKind::MethodCall(..)
+        | ExprKind::Tup(..)
+        | ExprKind::Binary(..)
+        | ExprKind::Unary(..)
+        | ExprKind::Lit(..)
+        | ExprKind::Cast(..)
+        | ExprKind::Type(..)
+        | ExprKind::DropTemps(..)
+        | ExprKind::If(..)
+        | ExprKind::Loop(..)
+        | ExprKind::Match(..)
+        | ExprKind::Closure(..)
+        | ExprKind::Block(..)
+        | ExprKind::Assign(..)
+        | ExprKind::AssignOp(..)
+        | ExprKind::Path(..)
+        | ExprKind::AddrOf(..)
+        | ExprKind::Break(..)
+        | ExprKind::Continue(..)
+        | ExprKind::Ret(..)
+        | ExprKind::InlineAsm(..)
+        | ExprKind::LlvmInlineAsm(..)
+        | ExprKind::Struct(..)
+        | ExprKind::Repeat(..)
+        | ExprKind::Yield(..) => true,
+    }
+}
+
+#[allow(clippy::needless_pass_by_value)]
+fn report(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
+    match state {
+        State::DerefMethod {
+            ty_changed_count,
+            is_final_ufcs,
+        } => {
+            let mut app = Applicability::MachineApplicable;
+            let (expr_str, expr_is_macro_call) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
+            let ty = cx.typeck_results().expr_ty(expr);
+            let (_, ref_count) = peel_mid_ty_refs(ty);
+            let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
+                // a deref call changing &T -> &U requires two deref operators the first time
+                // this occurs. One to remove the reference, a second to call the deref impl.
+                "*".repeat(ty_changed_count + 1)
+            } else {
+                "*".repeat(ty_changed_count)
+            };
+            let addr_of_str = if ty_changed_count < ref_count {
+                // Check if a reborrow from &mut T -> &T is required.
+                if data.target_mut == Mutability::Not && matches!(ty.kind(), ty::Ref(_, _, Mutability::Mut)) {
+                    "&*"
+                } else {
+                    ""
+                }
+            } else if data.target_mut == Mutability::Mut {
+                "&mut "
+            } else {
+                "&"
+            };
+
+            let expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
+                format!("({})", expr_str)
+            } else {
+                expr_str.into_owned()
+            };
+
+            span_lint_and_sugg(
+                cx,
+                EXPLICIT_DEREF_METHODS,
+                data.span,
+                match data.target_mut {
+                    Mutability::Not => "explicit `deref` method call",
+                    Mutability::Mut => "explicit `deref_mut` method call",
+                },
+                "try this",
+                format!("{}{}{}", addr_of_str, deref_str, expr_str),
+                app,
+            );
         },
-        _ => (),
     }
 }
index 6d3094ed6bfad5b6f7684db0e8c7d4caba4148a1..834136f910d9c98ac9886f33c2e800ec645b975d 100644 (file)
@@ -1,8 +1,7 @@
-use crate::utils::paths;
-use crate::utils::{
-    get_trait_def_id, is_allowed, is_automatically_derived, is_copy, match_def_path, span_lint_and_help,
-    span_lint_and_note, span_lint_and_then,
-};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_then};
+use clippy_utils::paths;
+use clippy_utils::ty::is_copy;
+use clippy_utils::{get_trait_def_id, is_allowed, is_automatically_derived, match_def_path};
 use if_chain::if_chain;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, NestedVisitorMap, Visitor};
index 56dc6d18a58f2dc2974ec43f716a596a6520c37a..ded0a0fff54b24573df89db5978904dbb71d2d1b 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{fn_def_id, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::fn_def_id;
 
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::Expr;
index 90b02d52f8a713da572e50cb94e87a8d97675de2..14338ac8fafea9066e7b4d3e49634e945160e813 100644 (file)
@@ -1,7 +1,6 @@
-use crate::utils::{
-    implements_trait, is_entrypoint_fn, is_expn_of, is_type_diagnostic_item, match_panic_def_id, method_chain_args,
-    return_ty, span_lint, span_lint_and_note,
-};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::{is_entrypoint_fn, is_expn_of, match_panic_def_id, method_chain_args, return_ty};
 use if_chain::if_chain;
 use itertools::Itertools;
 use rustc_ast::ast::{Async, AttrKind, Attribute, FnKind, FnRetTy, ItemKind};
@@ -584,7 +583,7 @@ fn has_needless_main(code: &str, edition: Edition) -> bool {
                                 let returns_nothing = match &sig.decl.output {
                                     FnRetTy::Default(..) => true,
                                     FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
-                                    _ => false,
+                                    FnRetTy::Ty(_) => false,
                                 };
 
                                 if returns_nothing && !is_async && !block.stmts.is_empty() {
index 19f56195ec1b483d693c8e20d6464ca4864c0fa9..1d291565ec19d3136e6aaff8a747dd08a2cc679c 100644 (file)
@@ -1,13 +1,14 @@
 //! Lint on unnecessary double comparisons. Some examples:
 
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::eq_expr_value;
+use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
 
-use crate::utils::{eq_expr_value, snippet_with_applicability, span_lint_and_sugg};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for double comparisons that could be simplified to a single expression.
     ///
index abbcaf43f4151adcdfc44c0954d6e797531667f4..5afdcb3c09f592aa8261524c079d1797375d367e 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
index 2aea00d883c41c22eeb26515df8f39b7dffc9308..7e7ec017bbbcbda0fb875a055c5e4c93055ec846 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{is_copy, match_def_path, paths, span_lint_and_note};
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::ty::is_copy;
+use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
index c0529a34cc411ed5fca26c63b8e4e0eefcca2b5d..746c1f6df916f4c65791033df1f8fa79c9e82eef 100644 (file)
@@ -1,3 +1,5 @@
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::match_type;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
@@ -6,8 +8,8 @@
 use rustc_span::source_map::Spanned;
 
 use crate::consts::{constant, Constant};
-use crate::utils::paths;
-use crate::utils::{match_type, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::paths;
 
 declare_clippy_lint! {
     /// **What it does:** Checks for calculation of subsecond microseconds or milliseconds
index 95123e6ff6fe2740b912e8438f426592dc3834d2..26984df9539774d64c624e3075db7dcb08dfef52 100644 (file)
@@ -1,12 +1,11 @@
 //! Lint on if expressions with an else if, but without a final else branch.
 
+use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use crate::utils::span_lint_and_help;
-
 declare_clippy_lint! {
     /// **What it does:** Checks for usage of if expressions with an `else if` branch,
     /// but without a final `else` branch.
index 077c3b75fb8c837508fef72290b42571f59715c1..c92984a98346d4c7a135cfb6db71079819f35436 100644 (file)
@@ -1,6 +1,6 @@
 //! lint when there is an enum with no variants
 
-use crate::utils::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
index 55575969927ba891e35cb2faebb9302622f26df6..25eb048448ca71b8c5a8ec4c95caa0a74c453207 100644 (file)
@@ -1,6 +1,8 @@
-use crate::utils::SpanlessEq;
-use crate::utils::{get_item_name, is_type_diagnostic_item, match_type, paths, snippet, snippet_opt};
-use crate::utils::{snippet_with_applicability, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
+use clippy_utils::ty::{is_type_diagnostic_item, match_type};
+use clippy_utils::SpanlessEq;
+use clippy_utils::{get_item_name, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
index aa235642ac31094e96e9f911d8fa519642b4eb45..7a98ae39d3ae9b0921b13b790aff2cf88a937cb3 100644 (file)
@@ -2,7 +2,7 @@
 //! don't fit into an `i32`
 
 use crate::consts::{miri_to_const, Constant};
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::util::IntTypeExt;
index 67a463538568e9adf619ce3add880796f21c81c4..0ecc0bc3eb60a3b2d5f1bfbf3d9fcbb17451618b 100644 (file)
@@ -1,7 +1,8 @@
 //! lint on enum variants that are prefixed or suffixed by the same characters
 
-use crate::utils::{camel_case, is_present_in_source};
-use crate::utils::{span_lint, span_lint_and_help};
+use clippy_utils::camel_case;
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::source::is_present_in_source;
 use rustc_ast::ast::{EnumDef, Item, ItemKind, VisibilityKind};
 use rustc_lint::{EarlyContext, EarlyLintPass, Lint};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
index 6308f6e2e7e9d6d8c825620ad644386dad8b5b6f..6d7046ac8b7b717037d230e9bfacf3825c6c94c6 100644 (file)
@@ -1,7 +1,7 @@
-use crate::utils::{
-    ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, implements_trait, in_macro, is_copy, is_expn_of,
-    multispan_sugg, snippet, span_lint, span_lint_and_then,
-};
+use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::ty::{implements_trait, is_copy};
+use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, higher, in_macro, is_expn_of};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, StmtKind};
index dbd1ff514f0e13292b6d7a8a0aa2efc7b2e220d6..5960261678119e1b1cb8d8aab957544771abe583 100644 (file)
@@ -1,10 +1,10 @@
+use clippy_utils::diagnostics::span_lint;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
 
 use crate::consts::{constant_simple, Constant};
-use crate::utils::span_lint;
 
 declare_clippy_lint! {
     /// **What it does:** Checks for erasing operations, e.g., `x * 0`.
index 972167575475e6048f101cee80594eecc1f6d034..3581ab41906f414f26e97311c5d8360c2c855f21 100644 (file)
@@ -1,3 +1,5 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::contains_ty;
 use rustc_hir::intravisit;
 use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node};
 use rustc_infer::infer::TyCtxtInferExt;
@@ -11,8 +13,6 @@
 use rustc_target::spec::abi::Abi;
 use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 
-use crate::utils::{contains_ty, span_lint};
-
 #[derive(Copy, Clone)]
 pub struct BoxedLocal {
     pub too_large_for_stack: u64,
@@ -186,7 +186,7 @@ fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
         }
     }
 
-    fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) { }
+    fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
 }
 
 impl<'a, 'tcx> EscapeDelegate<'a, 'tcx> {
index c461732fd3693dc1301f3cdbbced43026adc862c..99253555a95e36659b14d1caa114d587e20e17bb 100644 (file)
@@ -1,3 +1,9 @@
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::higher;
+use clippy_utils::higher::VecArgs;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::{implements_trait, type_is_unsafe_function};
+use clippy_utils::{is_adjusted, iter_input_pats};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{def_id, Expr, ExprKind, Param, PatKind, QPath};
@@ -6,13 +12,6 @@
 use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use crate::utils::{
-    implements_trait, is_adjusted, iter_input_pats, snippet_opt, span_lint_and_sugg, span_lint_and_then,
-    type_is_unsafe_function,
-};
-use clippy_utils::higher;
-use clippy_utils::higher::VecArgs;
-
 declare_clippy_lint! {
     /// **What it does:** Checks for closures which just call another function where
     /// the function can be called directly. `unsafe` functions or calls where types
index 83cee11c3a85921d8615326974d4254f2901dfd5..ea33a4d98fd2a2f7d74f2ece926aae691b47ba41 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{get_parent_expr, path_to_local, path_to_local_id, span_lint, span_lint_and_note};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
+use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id};
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -272,7 +273,7 @@ fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -
             .init
             .as_ref()
             .map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)),
-        _ => StopEarly::KeepGoing,
+        StmtKind::Item(..) => StopEarly::KeepGoing,
     }
 }
 
index 6f22f65deac80ec183d003c4b7ac0ebeb1f581df..6c9652fd87df476a827232151cf5e2b0b6c06e41 100644 (file)
@@ -1,7 +1,6 @@
-use crate::utils::{attr_by_name, in_macro, match_path_ast, span_lint_and_help};
-use rustc_ast::ast::{
-    AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind,
-};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{attr_by_name, in_macro, match_path_ast};
+use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::Span;
index 316f7484862803281cdcc2f96e9b3a06d5c87beb..60ad2e8ee1404fec06b5b71a5b9bc68c96a11b02 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{indent_of, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::indent_of;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind};
index 915859270009b27775b5656c67a7241783abc373..635b6d83eab72e8a31978a7ce43d612cfd772832 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{is_entrypoint_fn, match_def_path, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{is_entrypoint_fn, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind, Item, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
index f8038d06e50347ea90c317b7c194c7f69832ffe0..4146b7baa1021364d11083635242a18f2704b719 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{is_expn_of, match_function_call, paths, span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::{is_expn_of, match_function_call, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
index f466dddc13c2044d7320f04fac2cec42403da220..52a5a7acf0d0d6de09bf697446de09fea2edaeed 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{is_expn_of, is_type_diagnostic_item, match_panic_def_id, method_chain_args, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{is_expn_of, match_panic_def_id, method_chain_args};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
index c1c08597ee67004700cc2aef27b2e0c8e4ba39f7..0c59db360f9a24bfd6e2acca3b26440fbf691bef 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{match_def_path, paths, span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{match_def_path, paths, sugg};
 use if_chain::if_chain;
 use rustc_ast::util::parser::AssocOp;
 use rustc_errors::Applicability;
index 8e256f346841947d6c075f110aab2345afaa6871..1ca5c685a75c50419c8e85ad9e51773ea888eb0b 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{numeric_literal, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::numeric_literal;
 use if_chain::if_chain;
 use rustc_ast::ast::{self, LitFloatType, LitKind};
 use rustc_errors::Applicability;
index 086a791520fa8ec351ba4152d9089ab253f9c7c9..0b5f0379ce64417442c70483edc5826f1cd0b1d1 100644 (file)
@@ -2,7 +2,8 @@
     constant, constant_simple, Constant,
     Constant::{Int, F32, F64},
 };
-use crate::utils::{eq_expr_value, get_parent_expr, numeric_literal, span_lint_and_sugg, sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::{eq_expr_value, get_parent_expr, numeric_literal, sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
index fd6bf19db94c84c38dadef790aa69ca597cf0b48..a33f987b423ac27627469262cdc9f8b023993f84 100644 (file)
@@ -1,8 +1,8 @@
-use crate::utils::paths;
-use crate::utils::{
-    is_expn_of, is_type_diagnostic_item, last_path_segment, match_def_path, match_function_call, snippet, snippet_opt,
-    span_lint_and_then,
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::paths;
+use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{is_expn_of, last_path_segment, match_def_path, match_function_call};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
index 1bd16e6cce53a3a5fae6dab1fb18b936c1679682..b10e83c0ea819e5c1c891d310d6a3b7cd3288a72 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{differing_macro_contexts, snippet_opt, span_lint_and_help, span_lint_and_note};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
+use clippy_utils::differing_macro_contexts;
+use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
 use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
 use rustc_lint::{EarlyContext, EarlyLintPass};
index b644bb079908f821a0a699506aae9cb0ad5d132a..e5ec245e5029bca40396ab43226625f7bc9eaea7 100644 (file)
@@ -1,5 +1,6 @@
-use crate::utils::paths::INTO;
-use crate::utils::{match_def_path, meets_msrv, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::paths::INTO;
+use clippy_utils::{match_def_path, meets_msrv};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
index 0933f9830147c4f3d13f8757469d5df7cac80b17..3da5bc95b6db15e1aa49f45c03b111331befdf28 100644 (file)
@@ -1,3 +1,6 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{def, Expr, ExprKind, PrimTy, QPath, TyKind};
@@ -6,10 +9,6 @@
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::symbol::sym;
 
-use crate::utils::is_type_diagnostic_item;
-use crate::utils::span_lint_and_sugg;
-use crate::utils::sugg::Sugg;
-
 declare_clippy_lint! {
     /// **What it does:**
     /// Checks for function invocations of the form `primitive::from_str_radix(s, 10)`
index c474db06fe3fd577cf1b15151ac6ef2251a74f0a..730492fc7e3efafd787169327b883ec2d94608a7 100644 (file)
@@ -1,7 +1,9 @@
-use crate::utils::{
-    attr_by_name, attrs::is_proc_macro, is_must_use_ty, is_trait_impl_item, is_type_diagnostic_item, iter_input_pats,
-    match_def_path, must_use_attr, path_to_local, return_ty, snippet, snippet_opt, span_lint, span_lint_and_help,
-    span_lint_and_then, trait_ref_of_method, type_is_unsafe_function,
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_then};
+use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::ty::{is_must_use_ty, is_type_diagnostic_item, type_is_unsafe_function};
+use clippy_utils::{
+    attr_by_name, attrs::is_proc_macro, is_trait_impl_item, iter_input_pats, match_def_path, must_use_attr,
+    path_to_local, return_ty, trait_ref_of_method,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::Attribute;
index 9e1a8864a3ebe0ff6deff9c2cbec4c1bf19a7499..04730ace887c92868297202034a18da178571f8e 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::return_ty;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, FnDecl, HirId};
 use rustc_infer::infer::TyCtxtInferExt;
@@ -61,7 +62,7 @@ fn check_fn(
         if let FnKind::Closure = kind {
             return;
         }
-        let ret_ty = utils::return_ty(cx, hir_id);
+        let ret_ty = return_ty(cx, hir_id);
         if let Opaque(id, subst) = *ret_ty.kind() {
             let preds = cx.tcx.explicit_item_bounds(id);
             let mut is_future = false;
@@ -84,7 +85,7 @@ fn check_fn(
                     fulfillment_cx.select_all_or_error(&infcx)
                 });
                 if let Err(send_errors) = send_result {
-                    utils::span_lint_and_then(
+                    span_lint_and_then(
                         cx,
                         FUTURE_NOT_SEND,
                         span,
index cdd8a42e7cd12c003363c1e538b2f4564b79803b..cbcef567c53299d58f702f93a5ac9be9b7d4f499 100644 (file)
@@ -1,6 +1,9 @@
 //! lint on using `x.get(x.len() - 1)` instead of `x.last()`
 
-use crate::utils::{is_type_diagnostic_item, snippet_with_applicability, span_lint_and_sugg, SpanlessEq};
+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::SpanlessEq;
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
index 8501d34770201b1a80d77210dcb6999bbda9d7be..8bed5e1bf640edfc03a47023a64b72bd274bbbec 100644 (file)
@@ -1,3 +1,4 @@
+use clippy_utils::source::snippet;
 use if_chain::if_chain;
 use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -6,7 +7,8 @@
 use rustc_span::source_map::Span;
 
 use crate::consts::{constant_simple, Constant};
-use crate::utils::{clip, snippet, span_lint, unsext};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{clip, unsext};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for identity operations, e.g., `x + 0`.
index 58511c6d57c686dedd3c01f5f29a4a19d4fddec4..4aab43256bf0cadbc32f6857ef3d603cccd122b9 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{is_type_diagnostic_item, span_lint_and_help, SpanlessEq};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::SpanlessEq;
 use if_chain::if_chain;
 use rustc_hir::intravisit::{self as visit, NestedVisitorMap, Visitor};
 use rustc_hir::{Expr, ExprKind, MatchSource};
index 1194bd7e55e2566e5ad5c20f741d8888829e5e5f..6e9280c8c7e012b7c7d9556cdb5c2a5425691c9d 100644 (file)
@@ -1,4 +1,7 @@
-use crate::utils::{is_type_diagnostic_item, method_chain_args, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::method_chain_args;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, MatchSource, PatKind, QPath};
index b86d2e766566bd16391dde7b738d288d873050ac..c56f67df0618f3b6d47e1d2193ae1e613333db55 100644 (file)
@@ -1,13 +1,12 @@
 //! lint on if branches that could be swapped so no `!` operation is necessary
 //! on the condition
 
+use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_ast::ast::{BinOpKind, Expr, ExprKind, UnOp};
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use crate::utils::span_lint_and_help;
-
 declare_clippy_lint! {
     /// **What it does:** Checks for usage of `!` or `!=` in an if condition with an
     /// else branch.
diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs
new file mode 100644 (file)
index 0000000..0b5bf06
--- /dev/null
@@ -0,0 +1,117 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::{match_qpath, meets_msrv, parent_node_is_if_expr};
+use if_chain::if_chain;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+const IF_THEN_SOME_ELSE_NONE_MSRV: RustcVersion = RustcVersion::new(1, 50, 0);
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for if-else that could be written to `bool::then`.
+    ///
+    /// **Why is this bad?** Looks a little redundant. Using `bool::then` helps it have less lines of code.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    ///
+    /// ```rust
+    /// # let v = vec![0];
+    /// let a = if v.is_empty() {
+    ///     println!("true!");
+    ///     Some(42)
+    /// } else {
+    ///     None
+    /// };
+    /// ```
+    ///
+    /// Could be written:
+    ///
+    /// ```rust
+    /// # let v = vec![0];
+    /// let a = v.is_empty().then(|| {
+    ///     println!("true!");
+    ///     42
+    /// });
+    /// ```
+    pub IF_THEN_SOME_ELSE_NONE,
+    restriction,
+    "Finds if-else that could be written using `bool::then`"
+}
+
+pub struct IfThenSomeElseNone {
+    msrv: Option<RustcVersion>,
+}
+
+impl IfThenSomeElseNone {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
+
+impl_lint_pass!(IfThenSomeElseNone => [IF_THEN_SOME_ELSE_NONE]);
+
+impl LateLintPass<'_> for IfThenSomeElseNone {
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
+        if !meets_msrv(self.msrv.as_ref(), &IF_THEN_SOME_ELSE_NONE_MSRV) {
+            return;
+        }
+
+        if in_external_macro(cx.sess(), expr.span) {
+            return;
+        }
+
+        // We only care about the top-most `if` in the chain
+        if parent_node_is_if_expr(expr, cx) {
+            return;
+        }
+
+        if_chain! {
+            if let ExprKind::If(ref cond, ref then, Some(ref els)) = expr.kind;
+            if let ExprKind::Block(ref then_block, _) = then.kind;
+            if let Some(ref then_expr) = then_block.expr;
+            if let ExprKind::Call(ref then_call, [then_arg]) = then_expr.kind;
+            if let ExprKind::Path(ref then_call_qpath) = then_call.kind;
+            if match_qpath(then_call_qpath, &clippy_utils::paths::OPTION_SOME);
+            if let ExprKind::Block(ref els_block, _) = els.kind;
+            if els_block.stmts.is_empty();
+            if let Some(ref els_expr) = els_block.expr;
+            if let ExprKind::Path(ref els_call_qpath) = els_expr.kind;
+            if match_qpath(els_call_qpath, &clippy_utils::paths::OPTION_NONE);
+            then {
+                let cond_snip = snippet_with_macro_callsite(cx, cond.span, "[condition]");
+                let cond_snip = if matches!(cond.kind, ExprKind::Unary(_, _) | ExprKind::Binary(_, _, _)) {
+                    format!("({})", cond_snip)
+                } else {
+                    cond_snip.into_owned()
+                };
+                let arg_snip = snippet_with_macro_callsite(cx, then_arg.span, "");
+                let closure_body = if then_block.stmts.is_empty() {
+                    arg_snip.into_owned()
+                } else {
+                    format!("{{ /* snippet */ {} }}", arg_snip)
+                };
+                let help = format!(
+                    "consider using `bool::then` like: `{}.then(|| {})`",
+                    cond_snip,
+                    closure_body,
+                );
+                span_lint_and_help(
+                    cx,
+                    IF_THEN_SOME_ELSE_NONE,
+                    expr.span,
+                    "this could be simplified with `bool::then`",
+                    None,
+                    &help,
+                );
+            }
+        }
+    }
+
+    extract_msrv_attr!(LateContext);
+}
index b4f814e1dcccfb1894c73f14abf0ad2d268d0dcd..6863645a92dbeabbbc882296bdc08f6d5361f0ff 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{match_panic_def_id, snippet_opt, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::match_panic_def_id;
+use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
index 16e162badb5ee64a0172a3e23810490e13e2624b..5207c628987263776989fe329171f7d9072c1d23 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{in_macro, match_qpath, span_lint_and_sugg, SpanlessEq};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::{in_macro, match_qpath, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
index 48aef74e4d3c300039b4fa2fe2368ca8bf311e6f..d7ca24487a884ba0d374c91ea44bb93898a5099f 100644 (file)
@@ -1,3 +1,6 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
 use rustc_hir::{self as hir, ExprKind};
@@ -5,13 +8,10 @@
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::symbol::Symbol;
 
-use if_chain::if_chain;
-
-use crate::utils::{snippet, span_lint_and_sugg};
-
 declare_clippy_lint! {
-    /// **What it does:** Checks for struct constructors where the order of the field init
-    /// shorthand in the constructor is inconsistent with the order in the struct definition.
+    /// **What it does:** Checks for struct constructors where all fields are shorthand and
+    /// the order of the field init shorthand in the constructor is inconsistent
+    /// with the order in the struct definition.
     ///
     /// **Why is this bad?** Since the order of fields in a constructor doesn't affect the
     /// resulted instance as the below example indicates,
     /// let x = 1;
     /// let y = 2;
     ///
-    /// // This assertion never fails.
+    /// // This assertion never fails:
     /// assert_eq!(Foo { x, y }, Foo { y, x });
     /// ```
     ///
-    /// inconsistent order means nothing and just decreases readability and consistency.
+    /// inconsistent order can be confusing and decreases readability and consistency.
     ///
     /// **Known problems:** None.
     ///
@@ -42,6 +42,7 @@
     /// }
     /// let x = 1;
     /// let y = 2;
+    ///
     /// Foo { y, x };
     /// ```
     ///
@@ -107,7 +108,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
                     cx,
                     INCONSISTENT_STRUCT_CONSTRUCTOR,
                     expr.span,
-                    "inconsistent struct constructor",
+                    "struct constructor field order is inconsistent with struct definition field order",
                     "try",
                     sugg,
                     Applicability::MachineApplicable,
index c919ec097a2393a5c3c5ea1189e68278c7b54dfe..94d39019608f76b8d3c4b97d43b72a2143adb6c6 100644 (file)
@@ -1,7 +1,8 @@
 //! lint on indexing and slicing operations
 
 use crate::consts::{constant, Constant};
-use crate::utils::{higher, span_lint, span_lint_and_help};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::higher;
 use rustc_ast::ast::RangeLimits;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
index 7040ac3191f3c69c78d4c1ac711d05aaf53a4e3e..fb35bc1e78051c0bda11d30adbcec226c92c0b16 100644 (file)
@@ -1,9 +1,10 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::{implements_trait, match_type};
+use clippy_utils::{get_trait_def_id, higher, match_qpath, paths};
 use rustc_hir::{BorrowKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use crate::utils::{get_trait_def_id, higher, implements_trait, match_qpath, match_type, paths, span_lint};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for iteration that is guaranteed to be infinite.
     ///
index 005c461f105e60a7e3f4582788572d84cd81547b..5b2e70e3ce94f3d9c72477d951fafca9bc40ca0c 100644 (file)
@@ -1,6 +1,7 @@
 //! lint on inherent implementations
 
-use crate::utils::{in_macro, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::in_macro;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{def_id, Crate, Impl, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
index c1f3e1d9d685c13809e93f128303c914722cfba7..b023e13e846c7d19b6a090386109b65879854656 100644 (file)
@@ -1,14 +1,12 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::{get_trait_def_id, paths, return_ty, trait_ref_of_method};
 use if_chain::if_chain;
 use rustc_hir::{ImplItem, ImplItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
 
-use crate::utils::{
-    get_trait_def_id, implements_trait, is_type_diagnostic_item, paths, return_ty, span_lint_and_help,
-    trait_ref_of_method,
-};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for the definition of inherent methods with a signature of `to_string(&self) -> String`.
     ///
index 00acbd6cc3f7694525ebba2802f5883207b8a5bc..20f00bd51ba868dcf440314e8b11045e5764519c 100644 (file)
@@ -1,7 +1,7 @@
 //! checks for `#[inline]` on trait methods without bodies
 
-use crate::utils::span_lint_and_then;
-use crate::utils::sugg::DiagnosticBuilderExt;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg::DiagnosticBuilderExt;
 use rustc_ast::ast::Attribute;
 use rustc_errors::Applicability;
 use rustc_hir::{TraitFn, TraitItem, TraitItemKind};
index 260b8988d371113adf83b4801033215edc05e5c3..c4a1222b51fbe294854106e94ca409f7ff2fc7ec 100644 (file)
@@ -1,12 +1,12 @@
 //! lint on blocks unnecessarily using >= with a + 1 or - 1
 
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
 use rustc_ast::ast::{BinOpKind, Expr, ExprKind, Lit, LitKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use crate::utils::{snippet_opt, span_lint_and_sugg};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for usage of `x >= y + 1` or `x - 1 >= y` (and `<=`) in a block
     ///
index 39b4605e72f103b6024bcbc03c02c15fb2d77dc3..e5482f675e78b64295b9a32641a78e04e8e3ca76 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_help;
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
index 0927d218446ddb71c506d9cc19558c4fc265059e..c69571f32a2442bbaa2d07fdc64156fc502cc46a 100644 (file)
@@ -1,6 +1,6 @@
 //! lint when items are used after statements
 
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
 use rustc_ast::ast::{Block, ItemKind, StmtKind};
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
index a76595ed0897dd85ffa73afc66bfe79211d4392b..48dc5fefe99784d9e98f66994a069c478921f075 100644 (file)
@@ -1,5 +1,5 @@
 use crate::rustc_target::abi::LayoutOf;
-use crate::utils::span_lint_and_then;
+use clippy_utils::diagnostics::span_lint_and_then;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind};
index ab4cb33612d380f0cddb11b0d1b5eec75bf4c253..76584dc18222282c4ed6e25500858773eafee7e3 100644 (file)
@@ -1,6 +1,7 @@
 //! lint when there is a large size difference between variants on an enum
 
-use crate::utils::{snippet_opt, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_opt;
 use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind, VariantData};
 use rustc_lint::{LateContext, LateLintPass};
index 9a448ab125686cf2ce405a7ca9c1a84416b800c7..c46b98022c6cad6c76a877f69d5e0652c4d4a333 100644 (file)
@@ -1,13 +1,13 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet;
+use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::mir::interpret::ConstValue;
 use rustc_middle::ty::{self, ConstKind};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
-use if_chain::if_chain;
-
 use crate::rustc_target::abi::LayoutOf;
-use crate::utils::{snippet, span_lint_and_help};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for local arrays that may be too large.
index 1e1023b2743502e8d732eaf825e8c09f6414e9ec..717f2ea84f42af7037eb6fb2c851b71bdd6819ce 100644 (file)
@@ -1,7 +1,6 @@
-use crate::utils::{
-    get_item_name, get_parent_as_impl, is_allowed, snippet_with_applicability, span_lint, span_lint_and_sugg,
-    span_lint_and_then,
-};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{get_item_name, get_parent_as_impl, is_allowed};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_data_structures::fx::FxHashSet;
index 5863eef8a26f804db9a5a5d212cfaf061ac6bfba..2d7d9c9befbb2414f2701324320abdcc3049f91e 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{path_to_local_id, snippet, span_lint_and_then, visitors::LocalUsedVisitor};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::{path_to_local_id, visitors::LocalUsedVisitor};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
index 7e96dfcc7da0fef32a8e6f681b620b42649ad2c6..7e3a76ded2cfab0060d65da18d44822444a11f49 100644 (file)
@@ -1,3 +1,6 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::{is_must_use_ty, match_type};
+use clippy_utils::{is_must_use_func_call, paths};
 use if_chain::if_chain;
 use rustc_hir::{Local, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -5,8 +8,6 @@
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use crate::utils::{is_must_use_func_call, is_must_use_ty, match_type, paths, span_lint_and_help};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for `let _ = <expr>`
     /// where expr is #[must_use]
index b4c450bda5c728fa4d2f2cf93bd00a4a7440cd49..1c3841f8efd6feed9eb6d4801b8de63e7a1885e9 100644 (file)
@@ -42,7 +42,7 @@
 extern crate rustc_trait_selection;
 extern crate rustc_typeck;
 
-use crate::utils::parse_msrv;
+use clippy_utils::parse_msrv;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_lint::LintId;
 use rustc_session::Session;
@@ -231,6 +231,7 @@ macro_rules! extract_msrv_attr {
 mod if_let_mutex;
 mod if_let_some_result;
 mod if_not_else;
+mod if_then_some_else_none;
 mod implicit_return;
 mod implicit_saturating_sub;
 mod inconsistent_struct_constructor;
@@ -349,6 +350,7 @@ macro_rules! extract_msrv_attr {
 mod undropped_manually_drops;
 mod unicode;
 mod unit_return_expecting_ord;
+mod unit_types;
 mod unnamed_address;
 mod unnecessary_sort_by;
 mod unnecessary_wraps;
@@ -680,6 +682,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &if_let_mutex::IF_LET_MUTEX,
         &if_let_some_result::IF_LET_SOME_RESULT,
         &if_not_else::IF_NOT_ELSE,
+        &if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
         &implicit_return::IMPLICIT_RETURN,
         &implicit_saturating_sub::IMPLICIT_SATURATING_SUB,
         &inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR,
@@ -958,20 +961,20 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         &types::BOX_VEC,
         &types::IMPLICIT_HASHER,
         &types::INVALID_UPCAST_COMPARISONS,
-        &types::LET_UNIT_VALUE,
         &types::LINKEDLIST,
         &types::OPTION_OPTION,
         &types::RC_BUFFER,
         &types::REDUNDANT_ALLOCATION,
         &types::TYPE_COMPLEXITY,
-        &types::UNIT_ARG,
-        &types::UNIT_CMP,
         &types::VEC_BOX,
         &undropped_manually_drops::UNDROPPED_MANUALLY_DROPS,
         &unicode::INVISIBLE_CHARACTERS,
         &unicode::NON_ASCII_LITERAL,
         &unicode::UNICODE_NOT_NFC,
         &unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD,
+        &unit_types::LET_UNIT_VALUE,
+        &unit_types::UNIT_ARG,
+        &unit_types::UNIT_CMP,
         &unnamed_address::FN_ADDRESS_COMPARISONS,
         &unnamed_address::VTABLE_ADDRESS_COMPARISONS,
         &unnecessary_sort_by::UNNECESSARY_SORT_BY,
@@ -1082,8 +1085,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box map_clone::MapClone);
     store.register_late_pass(|| box map_err_ignore::MapErrIgnore);
     store.register_late_pass(|| box shadow::Shadow);
-    store.register_late_pass(|| box types::LetUnitValue);
-    store.register_late_pass(|| box types::UnitCmp);
+    store.register_late_pass(|| box unit_types::UnitTypes);
     store.register_late_pass(|| box loops::Loops);
     store.register_late_pass(|| box main_recursion::MainRecursion::default());
     store.register_late_pass(|| box lifetimes::Lifetimes);
@@ -1158,7 +1160,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box useless_conversion::UselessConversion::default());
     store.register_late_pass(|| box types::ImplicitHasher);
     store.register_late_pass(|| box fallible_impl_from::FallibleImplFrom);
-    store.register_late_pass(|| box types::UnitArg);
     store.register_late_pass(|| box double_comparison::DoubleComparisons);
     store.register_late_pass(|| box question_mark::QuestionMark);
     store.register_early_pass(|| box suspicious_operation_groupings::SuspiciousOperationGroupings);
@@ -1241,7 +1242,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box verbose_file_reads::VerboseFileReads);
     store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default());
     store.register_late_pass(|| box unnamed_address::UnnamedAddress);
-    store.register_late_pass(|| box dereference::Dereferencing);
+    store.register_late_pass(|| box dereference::Dereferencing::default());
     store.register_late_pass(|| box option_if_let_else::OptionIfLetElse);
     store.register_late_pass(|| box future_not_send::FutureNotSend);
     store.register_late_pass(|| box if_let_mutex::IfLetMutex);
@@ -1280,6 +1281,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| box redundant_slicing::RedundantSlicing);
     store.register_late_pass(|| box from_str_radix_10::FromStrRadix10);
     store.register_late_pass(|| box manual_map::ManualMap);
+    store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv));
 
     store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
         LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1295,6 +1297,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&exhaustive_items::EXHAUSTIVE_STRUCTS),
         LintId::of(&exit::EXIT),
         LintId::of(&float_literal::LOSSY_FLOAT_LITERAL),
+        LintId::of(&if_then_some_else_none::IF_THEN_SOME_ELSE_NONE),
         LintId::of(&implicit_return::IMPLICIT_RETURN),
         LintId::of(&indexing_slicing::INDEXING_SLICING),
         LintId::of(&inherent_impl::MULTIPLE_INHERENT_IMPL),
@@ -1411,11 +1414,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&trait_bounds::TYPE_REPETITION_IN_BOUNDS),
         LintId::of(&types::IMPLICIT_HASHER),
         LintId::of(&types::INVALID_UPCAST_COMPARISONS),
-        LintId::of(&types::LET_UNIT_VALUE),
         LintId::of(&types::LINKEDLIST),
         LintId::of(&types::OPTION_OPTION),
         LintId::of(&unicode::NON_ASCII_LITERAL),
         LintId::of(&unicode::UNICODE_NOT_NFC),
+        LintId::of(&unit_types::LET_UNIT_VALUE),
         LintId::of(&unnecessary_wraps::UNNECESSARY_WRAPS),
         LintId::of(&unnested_or_patterns::UNNESTED_OR_PATTERNS),
         LintId::of(&unused_self::UNUSED_SELF),
@@ -1704,12 +1707,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&types::BOX_VEC),
         LintId::of(&types::REDUNDANT_ALLOCATION),
         LintId::of(&types::TYPE_COMPLEXITY),
-        LintId::of(&types::UNIT_ARG),
-        LintId::of(&types::UNIT_CMP),
         LintId::of(&types::VEC_BOX),
         LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
         LintId::of(&unicode::INVISIBLE_CHARACTERS),
         LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
+        LintId::of(&unit_types::UNIT_ARG),
+        LintId::of(&unit_types::UNIT_CMP),
         LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
         LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
         LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
@@ -1930,8 +1933,8 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&transmute::TRANSMUTE_PTR_TO_REF),
         LintId::of(&types::BORROWED_BOX),
         LintId::of(&types::TYPE_COMPLEXITY),
-        LintId::of(&types::UNIT_ARG),
         LintId::of(&types::VEC_BOX),
+        LintId::of(&unit_types::UNIT_ARG),
         LintId::of(&unnecessary_sort_by::UNNECESSARY_SORT_BY),
         LintId::of(&unwrap::UNNECESSARY_UNWRAP),
         LintId::of(&useless_conversion::USELESS_CONVERSION),
@@ -2001,10 +2004,10 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         LintId::of(&transmute::WRONG_TRANSMUTE),
         LintId::of(&transmuting_null::TRANSMUTING_NULL),
         LintId::of(&types::ABSURD_EXTREME_COMPARISONS),
-        LintId::of(&types::UNIT_CMP),
         LintId::of(&undropped_manually_drops::UNDROPPED_MANUALLY_DROPS),
         LintId::of(&unicode::INVISIBLE_CHARACTERS),
         LintId::of(&unit_return_expecting_ord::UNIT_RETURN_EXPECTING_ORD),
+        LintId::of(&unit_types::UNIT_CMP),
         LintId::of(&unnamed_address::FN_ADDRESS_COMPARISONS),
         LintId::of(&unnamed_address::VTABLE_ADDRESS_COMPARISONS),
         LintId::of(&unused_io_amount::UNUSED_IO_AMOUNT),
index 3ac6e6cbbefcf49f1b924cbb8b7278f7ebea0204..e3b3fa21cabb4a5298d9ec2436e4a87c4476338f 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{in_macro, span_lint, trait_ref_of_method};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{in_macro, trait_ref_of_method};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::intravisit::{
     walk_fn_decl, walk_generic_param, walk_generics, walk_item, walk_param_bound, walk_poly_trait_ref, walk_ty,
index 87a957a9bd241728bd01fbaedaf06f08ee5836a7..7fd55151226b40c39d604e6216ea7640ed931022 100644 (file)
@@ -1,10 +1,11 @@
 //! Lints concerned with the grouping of digits with underscores in integral or
 //! floating-point literal expressions.
 
-use crate::utils::{
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{
     in_macro,
     numeric_literal::{NumericLiteral, Radix},
-    snippet_opt, span_lint_and_sugg,
 };
 use if_chain::if_chain;
 use rustc_ast::ast::{Expr, ExprKind, Lit, LitKind};
index 43e85538f281da7a7f35c882b94895a2651136a2..dda09fecdf90f5918fa396569c981f89f80359f7 100644 (file)
@@ -1,5 +1,6 @@
 use super::EMPTY_LOOP;
-use crate::utils::{is_in_panic_handler, is_no_std_crate, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{is_in_panic_handler, is_no_std_crate};
 
 use rustc_hir::{Block, Expr};
 use rustc_lint::LateContext;
index 8d98b940c66a95c3745c1c49dec67c2a74c9fa77..f14dbb1d642196ab913da32e7b03dbc0458ecc85 100644 (file)
@@ -1,7 +1,9 @@
 use super::{
     get_span_of_entire_for_loop, make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT_COUNTER_LOOP,
 };
-use crate::utils::{get_enclosing_block, is_integer_const, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{get_enclosing_block, is_integer_const};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_block, walk_expr};
index 1d778205a2ad1f3d118dd5f7461933a8387989b3..4871a03118739d15261d2a7bb557ea46f2a27447 100644 (file)
@@ -1,5 +1,6 @@
 use super::EXPLICIT_INTO_ITER_LOOP;
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
index 9683e59a3962d2acac1613a92f1cd864ee155af3..92aa2beb66d455690c6aa05a44f2074849b7afbc 100644 (file)
@@ -1,13 +1,14 @@
 use super::EXPLICIT_ITER_LOOP;
-use crate::utils::{match_trait_method, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type};
+use clippy_utils::{match_trait_method, paths};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Mutability};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty, TyS};
 use rustc_span::sym;
 
-use crate::utils::{is_type_diagnostic_item, match_type, paths};
-
 pub(super) fn check(cx: &LateContext<'_>, args: &[Expr<'_>], arg: &Expr<'_>, method_name: &str) {
     let should_lint = match method_name {
         "iter" | "iter_mut" => is_ref_iterable_type(cx, &args[0]),
index 6ee9b95a3b689035a544e65b1e1de2b7369ae98d..8f18f54119bd7a2d809a3087f8626cafd63b5791 100644 (file)
@@ -1,6 +1,9 @@
 use super::FOR_KV_MAP;
-use crate::utils::visitors::LocalUsedVisitor;
-use crate::utils::{is_type_diagnostic_item, match_type, multispan_sugg, paths, snippet, span_lint_and_then, sugg};
+use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type};
+use clippy_utils::visitors::LocalUsedVisitor;
+use clippy_utils::{paths, sugg};
 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
index db22d90a304bcac0bf04a121ee818ee8ea422cab..d49b0517dcf3eceef04c1c8905f08f2a9c7fd3fd 100644 (file)
@@ -1,5 +1,7 @@
 use super::FOR_LOOPS_OVER_FALLIBLES;
-use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_hir::{Expr, Pat};
 use rustc_lint::LateContext;
 use rustc_span::symbol::sym;
index cf78bbc49a3623a3f2e5b5a569fc7d19cd3da161..9148fbfd497af3e1c4de607ff583296135995e35 100644 (file)
@@ -1,10 +1,12 @@
 use super::ITER_NEXT_LOOP;
-use crate::utils::{match_trait_method, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_trait_method;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
+use rustc_span::sym;
 
 pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, expr: &Expr<'_>) -> bool {
-    if match_trait_method(cx, arg, &paths::ITERATOR) {
+    if is_trait_method(cx, arg, sym::Iterator) {
         span_lint(
             cx,
             ITER_NEXT_LOOP,
index 3d3ae6f3152a3b9b183403ad4bbefc49acc1cb7e..8d2b9cccba468421a7126532c52eab0ccdf10de9 100644 (file)
@@ -1,10 +1,12 @@
 use super::utils::make_iterator_snippet;
 use super::MANUAL_FLATTEN;
-use crate::utils::{is_ok_ctor, is_some_ctor, path_to_local_id, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{is_ok_ctor, is_some_ctor, path_to_local_id};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, StmtKind};
 use rustc_lint::LateContext;
+use rustc_middle::ty;
 use rustc_span::source_map::Span;
 
 /// Check for unnecessary `if let` usage in a for loop where only the `Some` or `Ok` variant of the
@@ -53,6 +55,13 @@ pub(super) fn check<'tcx>(
                 // Prepare the help message
                 let mut applicability = Applicability::MaybeIncorrect;
                 let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
+                let copied = match cx.typeck_results().expr_ty(match_expr).kind() {
+                    ty::Ref(_, inner, _) => match inner.kind() {
+                        ty::Ref(..) => ".copied()",
+                        _ => ""
+                    }
+                    _ => ""
+                };
 
                 span_lint_and_then(
                     cx,
@@ -60,7 +69,7 @@ pub(super) fn check<'tcx>(
                     span,
                     &msg,
                     |diag| {
-                        let sugg = format!("{}.flatten()", arg_snippet);
+                        let sugg = format!("{}{}.flatten()", arg_snippet, copied);
                         diag.span_suggestion(
                             arg.span,
                             "try",
index 11660a8fe0dfb836ddc0838df3137e411dfbd3e0..f5758b68f6021e38aca34bb2371bf015f53b0704 100644 (file)
@@ -1,8 +1,9 @@
 use super::{get_span_of_entire_for_loop, IncrementVisitor, InitializeVisitor, MANUAL_MEMCPY};
-use crate::utils::sugg::Sugg;
-use crate::utils::{
-    get_enclosing_block, higher, is_type_diagnostic_item, path_to_local, snippet, span_lint_and_sugg, sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{get_enclosing_block, higher, path_to_local, sugg};
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_errors::Applicability;
index 2a372c6307eabfa6417b7af4368c12708d5bb22f..202914919987c942b116b3a3fcc3e2e688f19f36 100644 (file)
@@ -18,7 +18,7 @@
 mod while_let_loop;
 mod while_let_on_iterator;
 
-use crate::utils::higher;
+use clippy_utils::higher;
 use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
index cb56512db60fee29b266b2372ec8daa699304e24..1425d50f56046c2fcc9abc8a31be988154e1bc74 100644 (file)
@@ -1,11 +1,11 @@
 use super::MUT_RANGE_BOUND;
-use crate::utils::{higher, path_to_local, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{higher, path_to_local};
 use if_chain::if_chain;
 use rustc_hir::{BindingAnnotation, Expr, HirId, Node, PatKind};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
-use rustc_middle::mir::FakeReadCause;
-use rustc_middle::ty;
+use rustc_middle::{mir::FakeReadCause, ty};
 use rustc_span::source_map::Span;
 use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 
@@ -108,7 +108,7 @@ fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId) {
         }
     }
 
-    fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _:HirId) { }
+    fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
 }
 
 impl MutatePairDelegate<'_, '_> {
index 92560c806295ccc01e9397f6f244637a920d60c3..5594fc7b046e738e48fa7c032f1760fe90cbee2a 100644 (file)
@@ -1,9 +1,9 @@
 use super::NEEDLESS_COLLECT;
-use crate::utils::sugg::Sugg;
-use crate::utils::{
-    is_type_diagnostic_item, match_trait_method, match_type, path_to_local_id, paths, snippet, span_lint_and_sugg,
-    span_lint_and_then,
-};
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type};
+use clippy_utils::{is_trait_method, path_to_local_id, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_block, walk_expr, NestedVisitorMap, Visitor};
@@ -23,7 +23,7 @@ fn check_needless_collect_direct_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCont
     if_chain! {
         if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind;
         if let ExprKind::MethodCall(ref chain_method, _, _, _) = args[0].kind;
-        if chain_method.ident.name == sym!(collect) && match_trait_method(cx, &args[0], &paths::ITERATOR);
+        if chain_method.ident.name == sym!(collect) && is_trait_method(cx, &args[0], sym::Iterator);
         if let Some(ref generic_args) = chain_method.args;
         if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
         then {
@@ -94,7 +94,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo
                     init: Some(ref init_expr), .. }
                 ) = stmt.kind;
                 if let ExprKind::MethodCall(ref method_name, _, &[ref iter_source], ..) = init_expr.kind;
-                if method_name.ident.name == sym!(collect) && match_trait_method(cx, &init_expr, &paths::ITERATOR);
+                if method_name.ident.name == sym!(collect) && is_trait_method(cx, &init_expr, sym::Iterator);
                 if let Some(ref generic_args) = method_name.args;
                 if let Some(GenericArg::Type(ref ty)) = generic_args.args.get(0);
                 if let ty = cx.typeck_results().node_type(ty.hir_id);
index 5f02e4b9d875dd907844c962459487fb500dfa65..3c40d54cb42106f33d92757dc01d86609ba915a7 100644 (file)
@@ -1,8 +1,10 @@
 use super::NEEDLESS_RANGE_LOOP;
-use crate::utils::visitors::LocalUsedVisitor;
-use crate::utils::{
-    contains_name, has_iter_method, higher, is_integer_const, match_trait_method, multispan_sugg, path_to_local_id,
-    paths, snippet, span_lint_and_then, sugg, SpanlessEq,
+use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::ty::has_iter_method;
+use clippy_utils::visitors::LocalUsedVisitor;
+use clippy_utils::{
+    contains_name, higher, is_integer_const, match_trait_method, path_to_local_id, paths, sugg, SpanlessEq,
 };
 use if_chain::if_chain;
 use rustc_ast::ast;
index 45e1001d755550399697dcaa064dcb0b5a5d60a8..f63a3761a0d16910a4cdbeaf9c1d9b693740f1de 100644 (file)
@@ -1,5 +1,5 @@
 use super::NEVER_LOOP;
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
 use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, Stmt, StmtKind};
 use rustc_lint::LateContext;
 use std::iter::{once, Iterator};
@@ -78,7 +78,7 @@ fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> {
     match stmt.kind {
         StmtKind::Semi(ref e, ..) | StmtKind::Expr(ref e, ..) => Some(e),
         StmtKind::Local(ref local) => local.init.as_deref(),
-        _ => None,
+        StmtKind::Item(..) => None,
     }
 }
 
index f3585830e4ae3189651ff15f0d11aef5c2405ee5..849d7ec718cfb9b6df141711b503701e4608c12d 100644 (file)
@@ -1,5 +1,7 @@
 use super::SAME_ITEM_PUSH;
-use crate::utils::{implements_trait, is_type_diagnostic_item, snippet_with_macro_callsite, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use if_chain::if_chain;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
index 38400c93c9ab205d1a9f6eeb8e63b279e9792122..8451c1c6130de48e5005a6a8b43d41b1e89d7b14 100644 (file)
@@ -1,5 +1,7 @@
 use super::{get_span_of_entire_for_loop, SINGLE_ELEMENT_LOOP};
-use crate::utils::{indent_of, single_segment_path, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::single_segment_path;
+use clippy_utils::source::{indent_of, snippet};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind};
index 9e38e17719aad116846c252a97d1c9bd00bb89bc..bb409c4853286b45087bdb26b40ea77a3d3e4796 100644 (file)
@@ -1,7 +1,5 @@
-use crate::utils::{
-    get_parent_expr, get_trait_def_id, has_iter_method, implements_trait, is_integer_const, path_to_local,
-    path_to_local_id, paths, sugg,
-};
+use clippy_utils::ty::{has_iter_method, implements_trait};
+use clippy_utils::{get_parent_expr, is_integer_const, path_to_local, path_to_local_id, sugg};
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
@@ -10,7 +8,7 @@
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
 use rustc_span::source_map::Span;
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::{sym, Symbol};
 use std::iter::Iterator;
 
 #[derive(Debug, PartialEq)]
@@ -316,7 +314,7 @@ pub(super) fn get_span_of_entire_for_loop(expr: &Expr<'_>) -> Span {
 /// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the
 /// actual `Iterator` that the loop uses.
 pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String {
-    let impls_iterator = get_trait_def_id(cx, &paths::ITERATOR).map_or(false, |id| {
+    let impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| {
         implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[])
     });
     if impls_iterator {
index 05e0a7225631c5421b602d06e63c443acf3130f4..cad9ff8489a9c3e8544b40acec051a2696c99f1f 100644 (file)
@@ -1,7 +1,7 @@
 use super::WHILE_IMMUTABLE_CONDITION;
 use crate::consts::constant;
-use crate::utils::span_lint_and_then;
-use crate::utils::usage::mutated_variables;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::usage::mutated_variables;
 use if_chain::if_chain;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::def::{DefKind, Res};
index 65d8f2f1111a34fd39051ebfa4511a3a56deb520..ffe8c0c5494b0f61aec63dae5585011f6fa0a235 100644 (file)
@@ -1,5 +1,6 @@
 use super::WHILE_LET_LOOP;
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
 use rustc_hir::{Block, Expr, ExprKind, MatchSource, StmtKind};
 use rustc_lint::{LateContext, LintContext};
index e5a47694faa4e79bbaeeba86a1d750980c2b8a69..57fc6250a9ae8d762cdf1578537e08d78e7cd14c 100644 (file)
@@ -1,9 +1,11 @@
 use super::utils::{LoopNestVisitor, Nesting};
 use super::WHILE_LET_ON_ITERATOR;
-use crate::utils::usage::mutated_variables;
-use crate::utils::{
-    get_enclosing_block, get_trait_def_id, implements_trait, is_refutable, last_path_segment, match_trait_method,
-    path_to_local, path_to_local_id, paths, snippet_with_applicability, span_lint_and_sugg,
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::usage::mutated_variables;
+use clippy_utils::{
+    get_enclosing_block, is_refutable, is_trait_method, last_path_segment, path_to_local, path_to_local_id,
 };
 use if_chain::if_chain;
 use rustc_errors::Applicability;
@@ -11,7 +13,6 @@
 use rustc_hir::{Expr, ExprKind, HirId, MatchSource, Node, PatKind};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
-
 use rustc_span::symbol::sym;
 
 pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
@@ -27,7 +28,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             // Don't lint when the iterator is recreated on every iteration
             if_chain! {
                 if let ExprKind::MethodCall(..) | ExprKind::Call(..) = iter_expr.kind;
-                if let Some(iter_def_id) = get_trait_def_id(cx, &paths::ITERATOR);
+                if let Some(iter_def_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
                 if implements_trait(cx, cx.typeck_results().expr_ty(iter_expr), iter_def_id, &[]);
                 then {
                     return;
@@ -36,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 
             let lhs_constructor = last_path_segment(qpath);
             if method_path.ident.name == sym::next
-                && match_trait_method(cx, match_expr, &paths::ITERATOR)
+                && is_trait_method(cx, match_expr, sym::Iterator)
                 && lhs_constructor.ident.name == sym::Some
                 && (pat_args.is_empty()
                     || !is_refutable(cx, &pat_args[0])
index 6d9c78393c8c4d2eee97df292a35372f02ee80b1..d573c29783876ab845504aa767444082c4171442 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{in_macro, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
+use clippy_utils::source::snippet;
 use hir::def::{DefKind, Res};
 use if_chain::if_chain;
 use rustc_ast::ast;
index 1b274c79d3820b5a948efb4578adb3bf220b0e3f..07d8a440aea4c440e1047c10a4b3c3c069710d7d 100644 (file)
@@ -1,10 +1,11 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::snippet;
+use clippy_utils::{is_entrypoint_fn, is_no_std_crate};
+use if_chain::if_chain;
 use rustc_hir::{Crate, Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 
-use crate::utils::{is_entrypoint_fn, is_no_std_crate, snippet, span_lint_and_help};
-use if_chain::if_chain;
-
 declare_clippy_lint! {
     /// **What it does:** Checks for recursion using the entrypoint.
     ///
index 2e2e693592c8853d22eaffafffd45f0f72992626..5d88ff3b99f3191d0705d43804778e014d7814e5 100644 (file)
@@ -1,5 +1,7 @@
-use crate::utils::paths::FUTURE_FROM_GENERATOR;
-use crate::utils::{match_function_call, position_before_rarrow, snippet_block, snippet_opt, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::match_function_call;
+use clippy_utils::paths::FUTURE_FROM_GENERATOR;
+use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
index ac1d51e1993b481581627a5c5b576954b55a6c81..ed157783b723f7e7e40e0566c4a785096689680e 100644 (file)
@@ -1,18 +1,14 @@
-use crate::{
-    map_unit_fn::OPTION_MAP_UNIT_FN,
-    matches::MATCH_AS_REF,
-    utils::{
-        can_partially_move_ty, is_allowed, is_type_diagnostic_item, match_def_path, match_var, paths,
-        peel_hir_expr_refs, peel_mid_ty_refs_is_mutable, snippet_with_applicability, snippet_with_context,
-        span_lint_and_sugg,
-    },
-};
+use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
+use clippy_utils::ty::{can_partially_move_ty, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
+use clippy_utils::{is_allowed, is_else_clause_of_if_let_else, match_def_path, match_var, paths, peel_hir_expr_refs};
 use rustc_ast::util::parser::PREC_POSTFIX;
 use rustc_errors::Applicability;
 use rustc_hir::{
     def::Res,
     intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
-    Arm, BindingAnnotation, Block, Expr, ExprKind, Mutability, Pat, PatKind, Path, QPath,
+    Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, Pat, PatKind, Path, QPath,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -55,8 +51,11 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             return;
         }
 
-        if let ExprKind::Match(scrutinee, [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }], _) =
-            expr.kind
+        if let ExprKind::Match(
+            scrutinee,
+            [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }],
+            match_kind,
+        ) = expr.kind
         {
             let (scrutinee_ty, ty_ref_count, ty_mutability) =
                 peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee));
@@ -129,7 +128,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or
             // it's being passed by value.
             let scrutinee = peel_hir_expr_refs(scrutinee).0;
-            let scrutinee_str = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
+            let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
             let scrutinee_str =
                 if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
                     format!("({})", scrutinee_str)
@@ -160,7 +159,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                             "|{}{}| {}",
                             annotation,
                             some_binding,
-                            snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app)
+                            snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
                         )
                     },
                 }
@@ -168,8 +167,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 // TODO: handle explicit reference annotations.
                 format!(
                     "|{}| {}",
-                    snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app),
-                    snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app)
+                    snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0,
+                    snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
                 )
             } else {
                 // Refutable bindings and mixed reference annotations can't be handled by `map`.
@@ -182,7 +181,12 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 expr.span,
                 "manual implementation of `Option::map`",
                 "try this",
-                format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str),
+                if matches!(match_kind, MatchSource::IfLetDesugar { .. }) && is_else_clause_of_if_let_else(cx.tcx, expr)
+                {
+                    format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str)
+                } else {
+                    format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str)
+                },
                 app,
             );
         }
index 7e6d4d3a2160bd055095e53160f6ab870a9afe8c..95261580b8e7413d9cd60caa45d6d460ec02d91d 100644 (file)
@@ -1,6 +1,8 @@
-use crate::utils::{meets_msrv, snippet_opt, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::meets_msrv;
+use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
-use rustc_ast::ast::{Attribute, Item, ItemKind, FieldDef, Variant, VariantData, VisibilityKind};
+use rustc_ast::ast::{Attribute, FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind};
 use rustc_attr as attr;
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
index efb05b8ffdf4f4a7a49cd752b197bda51e281159..9bfae602c407d26f6bf78f761f86f3eeccbfbbf7 100644 (file)
@@ -1,7 +1,7 @@
-use crate::utils::{
-    indent_of, is_type_diagnostic_item, match_qpath, path_to_local_id, paths, reindent_multiline, snippet_opt,
-    span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{match_qpath, path_to_local_id, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, PatKind};
index 42a92104a4919f28d259e18e940f1b90a36aaff8..9da37efddac98563d288e0185be253d5ef657c9b 100644 (file)
@@ -1,9 +1,8 @@
 use crate::consts::{constant, Constant};
-use crate::utils::usage::mutated_variables;
-use crate::utils::{
-    eq_expr_value, higher, match_def_path, meets_msrv, multispan_sugg, paths, snippet, span_lint_and_then,
-};
-
+use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::usage::mutated_variables;
+use clippy_utils::{eq_expr_value, higher, match_def_path, meets_msrv, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_hir::def::Res;
index b452225b5db6cc53864220b08d24beba9cea14a4..f296d6a1a15f5f3af7f8b2ad688e14ec96fdee47 100644 (file)
@@ -1,6 +1,9 @@
 use crate::consts::constant_simple;
-use crate::utils;
-use crate::utils::{path_to_local_id, sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::usage::contains_return_break_continue_macro;
+use clippy_utils::{in_constant, match_qpath, path_to_local_id, paths, sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Arm, Expr, ExprKind, Pat, PatKind};
@@ -42,7 +45,7 @@
 
 impl LateLintPass<'_> for ManualUnwrapOr {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if in_external_macro(cx.sess(), expr.span) {
+        if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
             return;
         }
         lint_manual_unwrap_or(cx, expr);
@@ -72,19 +75,19 @@ fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
             if let Some((idx, or_arm)) = arms.iter().enumerate().find(|(_, arm)|
                 match arm.pat.kind {
                     PatKind::Path(ref some_qpath) =>
-                        utils::match_qpath(some_qpath, &utils::paths::OPTION_NONE),
+                        match_qpath(some_qpath, &paths::OPTION_NONE),
                     PatKind::TupleStruct(ref err_qpath, &[Pat { kind: PatKind::Wild, .. }], _) =>
-                        utils::match_qpath(err_qpath, &utils::paths::RESULT_ERR),
+                        match_qpath(err_qpath, &paths::RESULT_ERR),
                     _ => false,
                 }
             );
             let unwrap_arm = &arms[1 - idx];
             if let PatKind::TupleStruct(ref unwrap_qpath, &[unwrap_pat], _) = unwrap_arm.pat.kind;
-            if utils::match_qpath(unwrap_qpath, &utils::paths::OPTION_SOME)
-                || utils::match_qpath(unwrap_qpath, &utils::paths::RESULT_OK);
+            if match_qpath(unwrap_qpath, &paths::OPTION_SOME)
+                || match_qpath(unwrap_qpath, &paths::RESULT_OK);
             if let PatKind::Binding(_, binding_hir_id, ..) = unwrap_pat.kind;
             if path_to_local_id(unwrap_arm.body, binding_hir_id);
-            if !utils::usage::contains_return_break_continue_macro(or_arm.body);
+            if !contains_return_break_continue_macro(or_arm.body);
             then {
                 Some(or_arm)
             } else {
@@ -96,21 +99,21 @@ fn applicable_or_arm<'a>(arms: &'a [Arm<'a>]) -> Option<&'a Arm<'a>> {
     if_chain! {
         if let ExprKind::Match(scrutinee, match_arms, _) = expr.kind;
         let ty = cx.typeck_results().expr_ty(scrutinee);
-        if let Some(case) = if utils::is_type_diagnostic_item(cx, ty, sym::option_type) {
+        if let Some(case) = if is_type_diagnostic_item(cx, ty, sym::option_type) {
             Some(Case::Option)
-        } else if utils::is_type_diagnostic_item(cx, ty, sym::result_type) {
+        } else if is_type_diagnostic_item(cx, ty, sym::result_type) {
             Some(Case::Result)
         } else {
             None
         };
         if let Some(or_arm) = applicable_or_arm(match_arms);
-        if let Some(or_body_snippet) = utils::snippet_opt(cx, or_arm.body.span);
-        if let Some(indent) = utils::indent_of(cx, expr.span);
+        if let Some(or_body_snippet) = snippet_opt(cx, or_arm.body.span);
+        if let Some(indent) = indent_of(cx, expr.span);
         if constant_simple(cx, cx.typeck_results(), or_arm.body).is_some();
         then {
             let reindented_or_body =
-                utils::reindent_multiline(or_body_snippet.into(), true, Some(indent));
-            utils::span_lint_and_sugg(
+                reindent_multiline(or_body_snippet.into(), true, Some(indent));
+            span_lint_and_sugg(
                 cx,
                 MANUAL_UNWRAP_OR, expr.span,
                 &format!("this pattern reimplements `{}`", case.unwrap_fn_path()),
index 4b685c09a0548771101789ac952da24cd873ce25..cfcc705eabc9c640ddef320af0565eb6f6fd2859 100644 (file)
@@ -1,7 +1,8 @@
-use crate::utils::paths;
-use crate::utils::{
-    is_copy, is_type_diagnostic_item, match_trait_method, remove_blocks, snippet_with_applicability, span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::remove_blocks;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -55,7 +56,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
             if args.len() == 2;
             if method.ident.name == sym::map;
             let ty = cx.typeck_results().expr_ty(&args[0]);
-            if is_type_diagnostic_item(cx, ty, sym::option_type) || match_trait_method(cx, e, &paths::ITERATOR);
+            if is_type_diagnostic_item(cx, ty, sym::option_type) || is_trait_method(cx, e, sym::Iterator);
             if let hir::ExprKind::Closure(_, _, body_id, _, _) = args[1].kind;
             let closure_body = cx.tcx.hir().body(body_id);
             let closure_expr = remove_blocks(&closure_body.value);
index 76fe8e776eafda01f6d642edb19270a5182ddba2..a6a63961be572608f29f4605131388d5af6f8420 100644 (file)
@@ -1,5 +1,4 @@
-use crate::utils::span_lint_and_help;
-
+use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_hir::{CaptureBy, Expr, ExprKind, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
index 9f9c108a85a0537610a14d98fb6547c43d9cc372..75191e1f9ee8d104670e67b2404472e87ba19e1e 100644 (file)
@@ -1,7 +1,6 @@
-use crate::utils::{
-    is_adjusted, is_type_diagnostic_item, match_path, match_trait_method, match_var, paths, remove_blocks,
-    span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{is_adjusted, is_trait_method, match_path, match_var, paths, remove_blocks};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Body, Expr, ExprKind, Pat, PatKind, QPath, StmtKind};
@@ -65,7 +64,7 @@ fn get_map_argument<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<&'a
         if let ExprKind::MethodCall(ref method, _, ref args, _) = expr.kind;
         if args.len() == 2 && method.ident.name == sym::map;
         let caller_ty = cx.typeck_results().expr_ty(&args[0]);
-        if match_trait_method(cx, expr, &paths::ITERATOR)
+        if is_trait_method(cx, expr, sym::Iterator)
             || is_type_diagnostic_item(cx, caller_ty, sym::result_type)
             || is_type_diagnostic_item(cx, caller_ty, sym::option_type);
         then {
index 01126e86199b41172d5087cba7fc7192cf4cf5c3..d4764b5ccffd6bf82b5a9fd5d00f74855659f22b 100644 (file)
@@ -1,4 +1,7 @@
-use crate::utils::{is_type_diagnostic_item, iter_input_pats, method_chain_args, snippet, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{iter_input_pats, method_chain_args};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
index 086dae9422f9b57184af6e39f796377ec285e54d..ccaa5e98c83a9e38677735784274a43013c31725 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{is_type_diagnostic_item, is_type_lang_item, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, LangItem, MatchSource};
index 8570cd724b2f0c78eeff3304d0cd5ea6b50f453b..3680429fed7d8709ba446ec993d6a39da6fd6d39 100644 (file)
@@ -1,26 +1,28 @@
 use crate::consts::{constant, miri_to_const, Constant};
-use crate::utils::sugg::Sugg;
-use crate::utils::visitors::LocalUsedVisitor;
-use crate::utils::{
-    expr_block, get_parent_expr, implements_trait, in_macro, indent_of, is_allowed, is_expn_of, is_refutable,
-    is_type_diagnostic_item, is_wild, match_qpath, match_type, meets_msrv, multispan_sugg, path_to_local,
-    path_to_local_id, peel_hir_pat_refs, peel_mid_ty_refs, peel_n_hir_expr_refs, remove_blocks, snippet, snippet_block,
-    snippet_opt, snippet_with_applicability, span_lint_and_help, span_lint_and_note, span_lint_and_sugg,
-    span_lint_and_then, strip_pat_refs,
+use clippy_utils::diagnostics::{
+    multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
 };
-use crate::utils::{paths, search_same, SpanlessEq, SpanlessHash};
+use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
+use clippy_utils::visitors::LocalUsedVisitor;
+use clippy_utils::{
+    get_parent_expr, in_macro, is_allowed, is_expn_of, is_refutable, is_wild, match_qpath, meets_msrv, path_to_local,
+    path_to_local_id, peel_hir_pat_refs, peel_n_hir_expr_refs, recurse_or_patterns, remove_blocks, strip_pat_refs,
+};
+use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::Applicability;
-use rustc_hir::def::CtorKind;
+use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::{
-    Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource, Mutability, Node, Pat,
-    PatKind, QPath, RangeEnd,
+    self as hir, Arm, BindingAnnotation, Block, BorrowKind, Expr, ExprKind, Guard, HirId, Local, MatchSource,
+    Mutability, Node, Pat, PatKind, PathSegment, QPath, RangeEnd, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Ty, TyS};
+use rustc_middle::ty::{self, Ty, TyS, VariantDef};
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::{Span, Spanned};
@@ -954,114 +956,181 @@ fn check_wild_err_arm<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm
     }
 }
 
-fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
-    let ty = cx.typeck_results().expr_ty(ex);
-    if !ty.is_enum() {
-        // If there isn't a nice closed set of possible values that can be conveniently enumerated,
-        // don't complain about not enumerating the mall.
-        return;
+enum CommonPrefixSearcher<'a> {
+    None,
+    Path(&'a [PathSegment<'a>]),
+    Mixed,
+}
+impl CommonPrefixSearcher<'a> {
+    fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
+        match path {
+            [path @ .., _] => self.with_prefix(path),
+            [] => (),
+        }
     }
 
+    fn with_prefix(&mut self, path: &'a [PathSegment<'a>]) {
+        match self {
+            Self::None => *self = Self::Path(path),
+            Self::Path(self_path)
+                if path
+                    .iter()
+                    .map(|p| p.ident.name)
+                    .eq(self_path.iter().map(|p| p.ident.name)) => {},
+            Self::Path(_) => *self = Self::Mixed,
+            Self::Mixed => (),
+        }
+    }
+}
+
+#[allow(clippy::too_many_lines)]
+fn check_wild_enum_match(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
+    let ty = cx.typeck_results().expr_ty(ex).peel_refs();
+    let adt_def = match ty.kind() {
+        ty::Adt(adt_def, _)
+            if adt_def.is_enum()
+                && !(is_type_diagnostic_item(cx, ty, sym::option_type)
+                    || is_type_diagnostic_item(cx, ty, sym::result_type)) =>
+        {
+            adt_def
+        },
+        _ => return,
+    };
+
     // First pass - check for violation, but don't do much book-keeping because this is hopefully
     // the uncommon case, and the book-keeping is slightly expensive.
     let mut wildcard_span = None;
     let mut wildcard_ident = None;
+    let mut has_non_wild = false;
     for arm in arms {
-        if let PatKind::Wild = arm.pat.kind {
-            wildcard_span = Some(arm.pat.span);
-        } else if let PatKind::Binding(_, _, ident, None) = arm.pat.kind {
-            wildcard_span = Some(arm.pat.span);
-            wildcard_ident = Some(ident);
+        match peel_hir_pat_refs(arm.pat).0.kind {
+            PatKind::Wild => wildcard_span = Some(arm.pat.span),
+            PatKind::Binding(_, _, ident, None) => {
+                wildcard_span = Some(arm.pat.span);
+                wildcard_ident = Some(ident);
+            },
+            _ => has_non_wild = true,
         }
     }
+    let wildcard_span = match wildcard_span {
+        Some(x) if has_non_wild => x,
+        _ => return,
+    };
 
-    if let Some(wildcard_span) = wildcard_span {
-        // Accumulate the variants which should be put in place of the wildcard because they're not
-        // already covered.
+    // Accumulate the variants which should be put in place of the wildcard because they're not
+    // already covered.
+    let mut missing_variants: Vec<_> = adt_def.variants.iter().collect();
 
-        let mut missing_variants = vec![];
-        if let ty::Adt(def, _) = ty.kind() {
-            for variant in &def.variants {
-                missing_variants.push(variant);
+    let mut path_prefix = CommonPrefixSearcher::None;
+    for arm in arms {
+        // Guards mean that this case probably isn't exhaustively covered. Technically
+        // this is incorrect, as we should really check whether each variant is exhaustively
+        // covered by the set of guards that cover it, but that's really hard to do.
+        recurse_or_patterns(arm.pat, |pat| {
+            let path = match &peel_hir_pat_refs(pat).0.kind {
+                PatKind::Path(path) => {
+                    #[allow(clippy::match_same_arms)]
+                    let id = match cx.qpath_res(path, pat.hir_id) {
+                        Res::Def(DefKind::Const | DefKind::ConstParam | DefKind::AnonConst, _) => return,
+                        Res::Def(_, id) => id,
+                        _ => return,
+                    };
+                    if arm.guard.is_none() {
+                        missing_variants.retain(|e| e.ctor_def_id != Some(id));
+                    }
+                    path
+                },
+                PatKind::TupleStruct(path, patterns, ..) => {
+                    if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p)) {
+                        let id = cx.qpath_res(path, pat.hir_id).def_id();
+                        missing_variants.retain(|e| e.ctor_def_id != Some(id));
+                    }
+                    path
+                },
+                PatKind::Struct(path, patterns, ..) => {
+                    if arm.guard.is_none() && patterns.iter().all(|p| !is_refutable(cx, p.pat)) {
+                        let id = cx.qpath_res(path, pat.hir_id).def_id();
+                        missing_variants.retain(|e| e.def_id != id);
+                    }
+                    path
+                },
+                _ => return,
+            };
+            match path {
+                QPath::Resolved(_, path) => path_prefix.with_path(path.segments),
+                QPath::TypeRelative(
+                    hir::Ty {
+                        kind: TyKind::Path(QPath::Resolved(_, path)),
+                        ..
+                    },
+                    _,
+                ) => path_prefix.with_prefix(path.segments),
+                _ => (),
             }
-        }
+        });
+    }
 
-        for arm in arms {
-            if arm.guard.is_some() {
-                // Guards mean that this case probably isn't exhaustively covered. Technically
-                // this is incorrect, as we should really check whether each variant is exhaustively
-                // covered by the set of guards that cover it, but that's really hard to do.
-                continue;
-            }
-            if let PatKind::Path(ref path) = arm.pat.kind {
-                if let QPath::Resolved(_, p) = path {
-                    missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
-                }
-            } else if let PatKind::TupleStruct(QPath::Resolved(_, p), ref patterns, ..) = arm.pat.kind {
-                // Some simple checks for exhaustive patterns.
-                // There is a room for improvements to detect more cases,
-                // but it can be more expensive to do so.
-                let is_pattern_exhaustive =
-                    |pat: &&Pat<'_>| matches!(pat.kind, PatKind::Wild | PatKind::Binding(.., None));
-                if patterns.iter().all(is_pattern_exhaustive) {
-                    missing_variants.retain(|e| e.ctor_def_id != Some(p.res.def_id()));
+    let format_suggestion = |variant: &VariantDef| {
+        format!(
+            "{}{}{}{}",
+            if let Some(ident) = wildcard_ident {
+                format!("{} @ ", ident.name)
+            } else {
+                String::new()
+            },
+            if let CommonPrefixSearcher::Path(path_prefix) = path_prefix {
+                let mut s = String::new();
+                for seg in path_prefix {
+                    s.push_str(&seg.ident.as_str());
+                    s.push_str("::");
                 }
+                s
+            } else {
+                let mut s = cx.tcx.def_path_str(adt_def.did);
+                s.push_str("::");
+                s
+            },
+            variant.ident.name,
+            match variant.ctor_kind {
+                CtorKind::Fn if variant.fields.len() == 1 => "(_)",
+                CtorKind::Fn => "(..)",
+                CtorKind::Const => "",
+                CtorKind::Fictive => "{ .. }",
             }
-        }
-
-        let mut suggestion: Vec<String> = missing_variants
-            .iter()
-            .map(|v| {
-                let suffix = match v.ctor_kind {
-                    CtorKind::Fn => "(..)",
-                    CtorKind::Const | CtorKind::Fictive => "",
-                };
-                let ident_str = if let Some(ident) = wildcard_ident {
-                    format!("{} @ ", ident.name)
-                } else {
-                    String::new()
-                };
-                // This path assumes that the enum type is imported into scope.
-                format!("{}{}{}", ident_str, cx.tcx.def_path_str(v.def_id), suffix)
-            })
-            .collect();
-
-        if suggestion.is_empty() {
-            return;
-        }
-
-        let mut message = "wildcard match will miss any future added variants";
+        )
+    };
 
-        if let ty::Adt(def, _) = ty.kind() {
-            if def.is_variant_list_non_exhaustive() {
-                message = "match on non-exhaustive enum doesn't explicitly match all known variants";
-                suggestion.push(String::from("_"));
-            }
-        }
+    match missing_variants.as_slice() {
+        [] => (),
+        [x] if !adt_def.is_variant_list_non_exhaustive() => span_lint_and_sugg(
+            cx,
+            MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+            wildcard_span,
+            "wildcard matches only a single variant and will also match any future added variants",
+            "try this",
+            format_suggestion(x),
+            Applicability::MaybeIncorrect,
+        ),
+        variants => {
+            let mut suggestions: Vec<_> = variants.iter().cloned().map(format_suggestion).collect();
+            let message = if adt_def.is_variant_list_non_exhaustive() {
+                suggestions.push("_".into());
+                "wildcard matches known variants and will also match future added variants"
+            } else {
+                "wildcard match will also match any future added variants"
+            };
 
-        if suggestion.len() == 1 {
-            // No need to check for non-exhaustive enum as in that case len would be greater than 1
             span_lint_and_sugg(
                 cx,
-                MATCH_WILDCARD_FOR_SINGLE_VARIANTS,
+                WILDCARD_ENUM_MATCH_ARM,
                 wildcard_span,
                 message,
                 "try this",
-                suggestion[0].clone(),
+                suggestions.join(" | "),
                 Applicability::MaybeIncorrect,
             )
-        };
-
-        span_lint_and_sugg(
-            cx,
-            WILDCARD_ENUM_MATCH_ARM,
-            wildcard_span,
-            message,
-            "try this",
-            suggestion.join(" | "),
-            Applicability::MaybeIncorrect,
-        )
-    }
+        },
+    };
 }
 
 // If the block contains only a `panic!` macro (as expression or statement)
@@ -1284,6 +1353,7 @@ fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option<bool> {
     }
 }
 
+#[allow(clippy::too_many_lines)]
 fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], expr: &Expr<'_>) {
     if in_macro(expr.span) || arms.len() != 1 || is_refutable(cx, arms[0].pat) {
         return;
@@ -1358,7 +1428,18 @@ fn check_match_single_binding<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[A
                         indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
                         cbrace_start = format!("{{\n{}", indent);
                     }
-                };
+                }
+                // If the parent is already an arm, and the body is another match statement,
+                // we need curly braces around suggestion
+                let parent_node_id = cx.tcx.hir().get_parent_node(expr.hir_id);
+                if let Node::Arm(arm) = &cx.tcx.hir().get(parent_node_id) {
+                    if let ExprKind::Match(..) = arm.body.kind {
+                        cbrace_end = format!("\n{}}}", indent);
+                        // Fix body indent due to the match
+                        indent = " ".repeat(indent_of(cx, bind_names).unwrap_or(0));
+                        cbrace_start = format!("{{\n{}", indent);
+                    }
+                }
                 (
                     expr.span,
                     format!(
@@ -1614,7 +1695,9 @@ fn cmp(&self, other: &Self) -> Ordering {
 
 mod redundant_pattern_match {
     use super::REDUNDANT_PATTERN_MATCHING;
-    use crate::utils::{match_qpath, match_trait_method, paths, snippet, span_lint_and_then};
+    use clippy_utils::diagnostics::span_lint_and_then;
+    use clippy_utils::source::snippet;
+    use clippy_utils::{is_trait_method, match_qpath, paths};
     use if_chain::if_chain;
     use rustc_ast::ast::LitKind;
     use rustc_errors::Applicability;
@@ -1679,7 +1762,7 @@ fn find_sugg_for_if_let<'tcx>(
             if keyword == "while";
             if let ExprKind::MethodCall(method_path, _, _, _) = op.kind;
             if method_path.ident.name == sym::next;
-            if match_trait_method(cx, op, &paths::ITERATOR);
+            if is_trait_method(cx, op, sym::Iterator);
             then {
                 return;
             }
index c71c2ee7d70afeaf8ab5e63718b781197ef49654..7895ba9f1e07687154eb16e46d063964a12546ef 100644 (file)
@@ -1,10 +1,12 @@
-use crate::utils::{match_def_path, paths, snippet, span_lint_and_then, walk_ptrs_ty_depth};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::walk_ptrs_ty_depth;
+use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-
 use std::iter;
 
 declare_clippy_lint! {
index d34f9761e26f9a0d26b29c03733217e9e5296858..c13802e395391a209523c195f164ee01b37612aa 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{match_def_path, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{match_def_path, paths};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
index 19087b02077142fb84d86a62c87beffa7ac844d7..426c108d89fb59c2d5240e3c931ab7ea9efe5051 100644 (file)
@@ -1,9 +1,10 @@
-use crate::utils::{
-    in_macro, match_def_path, match_qpath, meets_msrv, paths, snippet, snippet_with_applicability, span_lint_and_help,
-    span_lint_and_sugg, span_lint_and_then,
-};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::is_diagnostic_assoc_item;
+use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::{in_macro, match_def_path, match_qpath, meets_msrv, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
+use rustc_hir::def_id::DefId;
 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -194,13 +195,44 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'
     }
 }
 
+/// Returns true if the `def_id` associated with the `path` is recognized as a "default-equivalent"
+/// constructor from the std library
+fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<'_>) -> bool {
+    let std_types_symbols = &[
+        sym::string_type,
+        sym::vec_type,
+        sym::vecdeque_type,
+        sym::LinkedList,
+        sym::hashmap_type,
+        sym::BTreeMap,
+        sym::hashset_type,
+        sym::BTreeSet,
+        sym::BinaryHeap,
+    ];
+
+    if std_types_symbols
+        .iter()
+        .any(|symbol| is_diagnostic_assoc_item(cx, def_id, *symbol))
+    {
+        if let QPath::TypeRelative(_, ref method) = path {
+            if method.ident.name == sym::new {
+                return true;
+            }
+        }
+    }
+
+    false
+}
+
 fn check_replace_with_default(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'_>, expr_span: Span) {
     if let ExprKind::Call(ref repl_func, _) = src.kind {
         if_chain! {
             if !in_external_macro(cx.tcx.sess, expr_span);
             if let ExprKind::Path(ref repl_func_qpath) = repl_func.kind;
             if let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id();
-            if match_def_path(cx, repl_def_id, &paths::DEFAULT_TRAIT_METHOD);
+            if is_diagnostic_assoc_item(cx, repl_def_id, sym::Default)
+                || is_default_equivalent_ctor(cx, repl_def_id, repl_func_qpath);
+
             then {
                 span_lint_and_then(
                     cx,
index 5decb81d9f2e26ca461cb3d6f9aa9343341e3adb..0ba8a98a01851398f2558656ddb5f31cfcaba5d4 100644 (file)
@@ -1,8 +1,8 @@
 use super::{contains_return, BIND_INSTEAD_OF_MAP};
-use crate::utils::{
-    in_macro, match_qpath, match_type, method_calls, multispan_sugg_with_applicability, paths, remove_blocks, snippet,
-    snippet_with_macro_callsite, span_lint_and_sugg, span_lint_and_then, visitors::find_all_ret_expressions,
-};
+use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::{snippet, snippet_with_macro_callsite};
+use clippy_utils::ty::match_type;
+use clippy_utils::{in_macro, match_qpath, method_calls, paths, remove_blocks, visitors::find_all_ret_expressions};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
index 71a7e195e41cee1cb2163cdf19abe032ba0d6984..4f88f80a304bd6cbfaec64ebefa98b285adbd7c0 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{is_type_diagnostic_item, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs
new file mode 100644 (file)
index 0000000..c668fe5
--- /dev/null
@@ -0,0 +1,54 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{method_chain_args, single_segment_path};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_lint::Lint;
+use rustc_middle::ty;
+use rustc_span::sym;
+
+/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    info: &crate::methods::BinaryExprInfo<'_>,
+    chain_methods: &[&str],
+    lint: &'static Lint,
+    suggest: &str,
+) -> bool {
+    if_chain! {
+        if let Some(args) = method_chain_args(info.chain, chain_methods);
+        if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind;
+        if arg_char.len() == 1;
+        if let hir::ExprKind::Path(ref qpath) = fun.kind;
+        if let Some(segment) = single_segment_path(qpath);
+        if segment.ident.name == sym::Some;
+        then {
+            let mut applicability = Applicability::MachineApplicable;
+            let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
+
+            if *self_ty.kind() != ty::Str {
+                return false;
+            }
+
+            span_lint_and_sugg(
+                cx,
+                lint,
+                info.expr.span,
+                &format!("you should use the `{}` method", suggest),
+                "like this",
+                format!("{}{}.{}({})",
+                        if info.eq { "" } else { "!" },
+                        snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
+                        suggest,
+                        snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)),
+                applicability,
+            );
+
+            return true;
+        }
+    }
+
+    false
+}
diff --git a/clippy_lints/src/methods/chars_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_cmp_with_unwrap.rs
new file mode 100644 (file)
index 0000000..4275857
--- /dev/null
@@ -0,0 +1,44 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::method_chain_args;
+use clippy_utils::source::snippet_with_applicability;
+use if_chain::if_chain;
+use rustc_ast::ast;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_lint::Lint;
+
+/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`.
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    info: &crate::methods::BinaryExprInfo<'_>,
+    chain_methods: &[&str],
+    lint: &'static Lint,
+    suggest: &str,
+) -> bool {
+    if_chain! {
+        if let Some(args) = method_chain_args(info.chain, chain_methods);
+        if let hir::ExprKind::Lit(ref lit) = info.other.kind;
+        if let ast::LitKind::Char(c) = lit.node;
+        then {
+            let mut applicability = Applicability::MachineApplicable;
+            span_lint_and_sugg(
+                cx,
+                lint,
+                info.expr.span,
+                &format!("you should use the `{}` method", suggest),
+                "like this",
+                format!("{}{}.{}('{}')",
+                        if info.eq { "" } else { "!" },
+                        snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
+                        suggest,
+                        c),
+                applicability,
+            );
+
+            true
+        } else {
+            false
+        }
+    }
+}
diff --git a/clippy_lints/src/methods/chars_last_cmp.rs b/clippy_lints/src/methods/chars_last_cmp.rs
new file mode 100644 (file)
index 0000000..07bbc5c
--- /dev/null
@@ -0,0 +1,13 @@
+use crate::methods::chars_cmp;
+use rustc_lint::LateContext;
+
+use super::CHARS_LAST_CMP;
+
+/// Checks for the `CHARS_LAST_CMP` lint.
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
+    if chars_cmp::check(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") {
+        true
+    } else {
+        chars_cmp::check(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with")
+    }
+}
diff --git a/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_last_cmp_with_unwrap.rs
new file mode 100644 (file)
index 0000000..c29ee0e
--- /dev/null
@@ -0,0 +1,13 @@
+use crate::methods::chars_cmp_with_unwrap;
+use rustc_lint::LateContext;
+
+use super::CHARS_LAST_CMP;
+
+/// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`.
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
+    if chars_cmp_with_unwrap::check(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") {
+        true
+    } else {
+        chars_cmp_with_unwrap::check(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with")
+    }
+}
diff --git a/clippy_lints/src/methods/chars_next_cmp.rs b/clippy_lints/src/methods/chars_next_cmp.rs
new file mode 100644 (file)
index 0000000..a6701d8
--- /dev/null
@@ -0,0 +1,8 @@
+use rustc_lint::LateContext;
+
+use super::CHARS_NEXT_CMP;
+
+/// Checks for the `CHARS_NEXT_CMP` lint.
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
+    crate::methods::chars_cmp::check(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with")
+}
diff --git a/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs b/clippy_lints/src/methods/chars_next_cmp_with_unwrap.rs
new file mode 100644 (file)
index 0000000..28ede28
--- /dev/null
@@ -0,0 +1,8 @@
+use rustc_lint::LateContext;
+
+use super::CHARS_NEXT_CMP;
+
+/// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`.
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, info: &crate::methods::BinaryExprInfo<'_>) -> bool {
+    crate::methods::chars_cmp_with_unwrap::check(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with")
+}
index 4a130ed47db15bc00e6f3afc075c963f55a5a75d..edb6649b87b4da3dec83356492fa497947784304 100644 (file)
@@ -1,15 +1,23 @@
-use crate::utils::{is_copy, span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
+use clippy_utils::ty::is_copy;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty;
+use rustc_span::symbol::{sym, Symbol};
 use std::iter;
 
 use super::CLONE_DOUBLE_REF;
 use super::CLONE_ON_COPY;
 
 /// Checks for the `CLONE_ON_COPY` lint.
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'_>) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
+    if !(args.len() == 1 && method_name == sym::clone) {
+        return;
+    }
+    let arg = &args[0];
+    let arg_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
     let ty = cx.typeck_results().expr_ty(expr);
     if let ty::Ref(_, inner, _) = arg_ty.kind() {
         if let ty::Ref(_, innermost, _) = inner.kind() {
index 3d5a68d69d7d2d9bbc510c70131bfbb215f4eba4..6417bc81304739e9c72ac75a9fb1c9d4045e097f 100644 (file)
@@ -1,13 +1,20 @@
-use crate::utils::{is_type_diagnostic_item, match_type, paths, snippet_with_macro_callsite, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::paths;
+use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::symbol::sym;
+use rustc_span::symbol::{sym, Symbol};
 
 use super::CLONE_ON_REF_PTR;
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
+    if !(args.len() == 1 && method_name == sym::clone) {
+        return;
+    }
+    let arg = &args[0];
     let obj_ty = cx.typeck_results().expr_ty(arg).peel_refs();
 
     if let ty::Adt(_, subst) = obj_ty.kind() {
index 6866e9c652ab37d9c5202f0f95a2e7a62dc0ff38..e7bffa66b3fcb1627618cb2aecb4d28268a13381 100644 (file)
@@ -1,4 +1,7 @@
-use crate::utils::{is_expn_of, is_type_diagnostic_item, snippet, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_expn_of;
+use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::ty::is_type_diagnostic_item;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
index 90b781bd9d1905015998d3bd7df81bc067ae5008..64531b29ade2f6c26fc61b4f439e903dc3151ff6 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{is_type_diagnostic_item, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_span::sym;
index b03835f97e634728bf554d2b415f04c17520ccc2..39d2f15dbc8bab844c54d6cd469afca1abca4865 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{get_parent_expr, match_type, paths, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::match_type;
+use clippy_utils::{get_parent_expr, paths};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
index 8da867fce515cde5d051ce25b10043bb05f584d5..1588eec88824bfdfc97ebee00565fe846a81f179 100644 (file)
@@ -1,18 +1,15 @@
-use crate::utils::{match_trait_method, paths, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_trait_method;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
+use rustc_span::sym;
 
 use super::FILTER_MAP;
 
 /// lint use of `filter().flat_map()` for `Iterators`
-pub(super) fn check<'tcx>(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx hir::Expr<'_>,
-    _filter_args: &'tcx [hir::Expr<'_>],
-    _map_args: &'tcx [hir::Expr<'_>],
-) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
     // lint if caller of `.filter().flat_map()` is an Iterator
-    if match_trait_method(cx, expr, &paths::ITERATOR) {
+    if is_trait_method(cx, expr, sym::Iterator) {
         let msg = "called `filter(..).flat_map(..)` on an `Iterator`";
         let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
                     and filtering by returning `iter::empty()`";
index f559160004cb1f56ad61fb21a5aa4a427c7e2b36..2cb476acb2b3c91649b7751f95bb1de3df8a7466 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{match_trait_method, path_to_local_id, paths, snippet, span_lint_and_sugg, SpanlessEq};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::{is_trait_method, path_to_local_id, SpanlessEq};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -15,7 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, is_
     if_chain! {
         if let ExprKind::MethodCall(_, _, [map_recv, map_arg], map_span) = expr.kind;
         if let ExprKind::MethodCall(_, _, [_, filter_arg], filter_span) = map_recv.kind;
-        if match_trait_method(cx, map_recv, &paths::ITERATOR);
+        if is_trait_method(cx, map_recv, sym::Iterator);
 
         // filter(|x| ...is_some())...
         if let ExprKind::Closure(_, _, filter_body_id, ..) = filter_arg.kind;
index a6db138623a8c6e7d5243652d4df89fb25e80b26..741b1e7e361255c09560bbdadd26ca46e32963b7 100644 (file)
@@ -1,18 +1,15 @@
-use crate::utils::{match_trait_method, paths, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_trait_method;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
+use rustc_span::sym;
 
 use super::FILTER_MAP;
 
 /// lint use of `filter_map().flat_map()` for `Iterators`
-pub(super) fn check<'tcx>(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx hir::Expr<'_>,
-    _filter_args: &'tcx [hir::Expr<'_>],
-    _map_args: &'tcx [hir::Expr<'_>],
-) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
     // lint if caller of `.filter_map().flat_map()` is an Iterator
-    if match_trait_method(cx, expr, &paths::ITERATOR) {
+    if is_trait_method(cx, expr, sym::Iterator) {
         let msg = "called `filter_map(..).flat_map(..)` on an `Iterator`";
         let hint = "this is more succinctly expressed by calling `.flat_map(..)` \
                     and filtering by returning `iter::empty()`";
index 9e646360a40c333a74c0f1c2d95b7ed0b984ad27..80598d88508483323fafe84fa26b161b79774ef0 100644 (file)
@@ -1,9 +1,10 @@
-use crate::utils::{match_qpath, match_trait_method, path_to_local_id, paths, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
+use rustc_span::{source_map::Span, sym};
 
 use super::FILTER_MAP_IDENTITY;
 
@@ -13,7 +14,7 @@ pub(super) fn check(
     filter_map_args: &[hir::Expr<'_>],
     filter_map_span: Span,
 ) {
-    if match_trait_method(cx, expr, &paths::ITERATOR) {
+    if is_trait_method(cx, expr, sym::Iterator) {
         let arg_node = &filter_map_args[1].kind;
 
         let apply_lint = |message: &str| {
index d015b4c7b385e85381ecd8c9876a3a7294c9bf18..713bbf258370d9ac08b1cf67d288e173abe4dd14 100644 (file)
@@ -1,18 +1,15 @@
-use crate::utils::{match_trait_method, paths, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_trait_method;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
+use rustc_span::sym;
 
 use super::FILTER_MAP;
 
 /// lint use of `filter_map().map()` for `Iterators`
-pub(super) fn check<'tcx>(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx hir::Expr<'_>,
-    _filter_args: &'tcx [hir::Expr<'_>],
-    _map_args: &'tcx [hir::Expr<'_>],
-) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
     // lint if caller of `.filter_map().map()` is an Iterator
-    if match_trait_method(cx, expr, &paths::ITERATOR) {
+    if is_trait_method(cx, expr, sym::Iterator) {
         let msg = "called `filter_map(..).map(..)` on an `Iterator`";
         let hint = "this is more succinctly expressed by only calling `.filter_map(..)` instead";
         span_lint_and_help(cx, FILTER_MAP, expr.span, msg, None, hint);
index a789df922ffdbfb2327419d867438ffbe2466304..ba57abd16c9269046362c76242abeb54f16a3f05 100644 (file)
@@ -1,8 +1,11 @@
-use crate::utils::{match_trait_method, meets_msrv, paths, snippet, span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::source::snippet;
+use clippy_utils::{is_trait_method, meets_msrv};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_semver::RustcVersion;
+use rustc_span::sym;
 
 use super::FILTER_MAP_NEXT;
 
@@ -14,7 +17,7 @@ pub(super) fn check<'tcx>(
     filter_args: &'tcx [hir::Expr<'_>],
     msrv: Option<&RustcVersion>,
 ) {
-    if match_trait_method(cx, expr, &paths::ITERATOR) {
+    if is_trait_method(cx, expr, sym::Iterator) {
         if !meets_msrv(msrv, &FILTER_MAP_NEXT_MSRV) {
             return;
         }
index 81619e73017f26a07892e72bd41a3ac01c9f5698..6cd24334414b4c33be2ee4867fddfe03649c216a 100644 (file)
@@ -1,14 +1,17 @@
-use crate::utils::{match_trait_method, paths, snippet, span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::is_trait_method;
+use clippy_utils::source::snippet;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
+use rustc_span::sym;
 
 use super::FILTER_NEXT;
 
 /// lint use of `filter().next()` for `Iterators`
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, filter_args: &'tcx [hir::Expr<'_>]) {
     // lint if caller of `.filter().next()` is an Iterator
-    if match_trait_method(cx, expr, &paths::ITERATOR) {
+    if is_trait_method(cx, expr, sym::Iterator) {
         let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
                    `.find(..)` instead";
         let filter_snippet = snippet(cx, filter_args[1].span, "..");
index ce3194f8a2373ef809cacc548beee4938b7ef04f..034ea6c6562776dc6c5c071257ad2967783c7853 100644 (file)
@@ -1,9 +1,10 @@
-use crate::utils::{match_qpath, match_trait_method, paths, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::{is_trait_method, match_qpath, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
+use rustc_span::{source_map::Span, sym};
 
 use super::FLAT_MAP_IDENTITY;
 
@@ -14,7 +15,7 @@ pub(super) fn check<'tcx>(
     flat_map_args: &'tcx [hir::Expr<'_>],
     flat_map_span: Span,
 ) {
-    if match_trait_method(cx, expr, &paths::ITERATOR) {
+    if is_trait_method(cx, expr, sym::Iterator) {
         let arg_node = &flat_map_args[1].kind;
 
         let apply_lint = |message: &str| {
index e50d0a3340026d466f521ca5660bbe65c6935405..15cf567431387c3e0c57aefb9fac3679a174cd64 100644 (file)
@@ -1,19 +1,24 @@
-use crate::utils::{get_trait_def_id, implements_trait, paths, span_lint_and_sugg, sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{get_trait_def_id, match_qpath, paths, sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
+use rustc_hir::ExprKind;
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::ty::Ty;
+use rustc_span::sym;
 
 use super::FROM_ITER_INSTEAD_OF_COLLECT;
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
-    let ty = cx.typeck_results().expr_ty(expr);
-    let arg_ty = cx.typeck_results().expr_ty(&args[0]);
-
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>], func_kind: &ExprKind<'_>) {
     if_chain! {
+        if let hir::ExprKind::Path(path) = func_kind;
+        if match_qpath(path, &["from_iter"]);
+        let ty = cx.typeck_results().expr_ty(expr);
+        let arg_ty = cx.typeck_results().expr_ty(&args[0]);
         if let Some(from_iter_id) = get_trait_def_id(cx, &paths::FROM_ITERATOR);
-        if let Some(iter_id) = get_trait_def_id(cx, &paths::ITERATOR);
+        if let Some(iter_id) = cx.tcx.get_diagnostic_item(sym::Iterator);
 
         if implements_trait(cx, ty, from_iter_id, &[]) && implements_trait(cx, arg_ty, iter_id, &[]);
         then {
index e157db2712a9ac143c9475dab5005e7abf215a22..b3a9743c61475d6ddba26d563bfae6dbbcffa291 100644 (file)
@@ -1,7 +1,8 @@
-use crate::methods::derefs_to_slice;
-use crate::utils::{
-    get_parent_expr, is_type_diagnostic_item, match_type, paths, snippet_with_applicability, span_lint_and_sugg,
-};
+use super::utils::derefs_to_slice;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type};
+use clippy_utils::{get_parent_expr, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
index a769493d11d3f7f6d9424730b0b5e27cbe80dd44..04461ad5c3a6f245acf7e41e67c4dda91376c8f7 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_sugg;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
index 3045b09c2389f4890a9b1543cc3ee89c5cd0f6ca..950ec62c9fe4f107199ae675cde17a98f7dfaa63 100644 (file)
@@ -1,20 +1,24 @@
-use super::INEFFICIENT_TO_STRING;
-use crate::utils::{
-    is_type_diagnostic_item, match_def_path, paths, snippet_with_applicability, span_lint_and_then, walk_ptrs_ty_depth,
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::{is_type_diagnostic_item, walk_ptrs_ty_depth};
+use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
-use rustc_span::sym;
+use rustc_span::symbol::{sym, Symbol};
+
+use super::INEFFICIENT_TO_STRING;
 
 /// Checks for the `INEFFICIENT_TO_STRING` lint
-pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr<'_>, arg_ty: Ty<'tcx>) {
+pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
     if_chain! {
+        if args.len() == 1 && method_name == sym!(to_string);
         if let Some(to_string_meth_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id);
         if match_def_path(cx, to_string_meth_did, &paths::TO_STRING_METHOD);
         if let Some(substs) = cx.typeck_results().node_substs_opt(expr.hir_id);
+        let arg_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
         let self_ty = substs.type_at(0);
         let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty);
         if deref_count >= 1;
@@ -31,7 +35,7 @@ pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &hir::Expr
                         self_ty, deref_self_ty
                     ));
                     let mut applicability = Applicability::MachineApplicable;
-                    let arg_snippet = snippet_with_applicability(cx, arg.span, "..", &mut applicability);
+                    let arg_snippet = snippet_with_applicability(cx, args[0].span, "..", &mut applicability);
                     diag.span_suggestion(
                         expr.span,
                         "try dereferencing the receiver",
index 959457a5bfc960c9fb7855b895297cd616e0bf00..7fd3ef1a622a0b1a12453f9614096df53ee42b17 100644 (file)
@@ -1,14 +1,14 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_trait_method;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
-
-use crate::utils::{match_trait_method, paths, span_lint_and_help};
+use rustc_span::{source_map::Span, sym};
 
 use super::INSPECT_FOR_EACH;
 
 /// lint use of `inspect().for_each()` for `Iterators`
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, inspect_span: Span) {
-    if match_trait_method(cx, expr, &paths::ITERATOR) {
+    if is_trait_method(cx, expr, sym::Iterator) {
         let msg = "called `inspect(..).for_each(..)` on an `Iterator`";
         let hint = "move the code from `inspect(..)` to `for_each(..)` and remove the `inspect(..)`";
         span_lint_and_help(
index 1e8315dbee25eb90e9d40c957235b6877980b8ca..da13b4ba37a562f6c06d2e39714cb3a90a8e0278 100644 (file)
@@ -1,30 +1,43 @@
-use crate::utils::{has_iter_method, match_trait_method, paths, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::ty::has_iter_method;
+use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
 use rustc_span::source_map::Span;
-use rustc_span::symbol::Symbol;
+use rustc_span::symbol::{sym, Symbol};
 
 use super::INTO_ITER_ON_REF;
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, self_ref_ty: Ty<'_>, method_span: Span) {
-    if !match_trait_method(cx, expr, &paths::INTO_ITERATOR) {
-        return;
-    }
-    if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ref_ty) {
-        span_lint_and_sugg(
-            cx,
-            INTO_ITER_ON_REF,
-            method_span,
-            &format!(
-                "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`",
-                method_name, kind,
-            ),
-            "call directly",
-            method_name.to_string(),
-            Applicability::MachineApplicable,
-        );
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    expr: &hir::Expr<'_>,
+    method_span: Span,
+    method_name: Symbol,
+    args: &[hir::Expr<'_>],
+) {
+    let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
+    if_chain! {
+        if let ty::Ref(..) = self_ty.kind();
+        if method_name == sym::into_iter;
+        if is_trait_method(cx, expr, sym::IntoIterator);
+        if let Some((kind, method_name)) = ty_has_iter_method(cx, self_ty);
+        then {
+            span_lint_and_sugg(
+                cx,
+                INTO_ITER_ON_REF,
+                method_span,
+                &format!(
+                    "this `.into_iter()` call is equivalent to `.{}()` and will not consume the `{}`",
+                    method_name, kind,
+                ),
+                "call directly",
+                method_name.to_string(),
+                Applicability::MachineApplicable,
+            );
+        }
     }
 }
 
index c3e48ffa5fae4de57a7652307bd97d7b17e021f8..848f47e39f6b48e1ae8e4eda297506f21f98aa96 100644 (file)
@@ -1,5 +1,6 @@
-use crate::methods::derefs_to_slice;
-use crate::utils::{is_type_diagnostic_item, span_lint_and_sugg};
+use crate::methods::utils::derefs_to_slice;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
index 869440e0165b2a7973705527e48ec729858215e1..e394a8fe8195e6da741e818843ac79ae3653de09 100644 (file)
@@ -1,6 +1,8 @@
-use crate::methods::derefs_to_slice;
-use crate::utils::{is_type_diagnostic_item, match_type, paths, snippet_with_applicability, span_lint_and_sugg};
-
+use super::utils::derefs_to_slice;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::paths;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type};
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
index 3c03a949cfed09138abaedf02b9f500db27ca92b..e9b37b6f2bd9360f715a0d5b8a7a4215546a8286 100644 (file)
@@ -1,5 +1,8 @@
-use crate::methods::derefs_to_slice;
-use crate::utils::{get_parent_expr, higher, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_sugg};
+use super::utils::derefs_to_slice;
+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::{get_parent_expr, higher};
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_errors::Applicability;
@@ -44,12 +47,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, ite
                 );
             }
         }
-    } else if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(caller_expr), sym::vec_type)
-        || matches!(
-            &cx.typeck_results().expr_ty(caller_expr).peel_refs().kind(),
-            ty::Array(_, _)
-        )
-    {
+    } else if is_vec_or_array(cx, caller_expr) {
         // caller is a Vec or an Array
         let mut applicability = Applicability::MachineApplicable;
         span_lint_and_sugg(
@@ -66,3 +64,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, ite
         );
     }
 }
+
+fn is_vec_or_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool {
+    is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::vec_type)
+        || matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
+}
index cc3e56ea87277de289d95e242b81a32d62400cf5..c46af427b3c67e1a3aed4245eddce33a010b8749 100644 (file)
@@ -1,6 +1,7 @@
-use crate::methods::derefs_to_slice;
+use super::utils::derefs_to_slice;
 use crate::methods::iter_nth_zero;
-use crate::utils::{is_type_diagnostic_item, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_span::symbol::sym;
index 247192d81f3ec58f33340eedcecce5511655d8d4..a12f672739c7b6172eb164698b2cbb2f01987732 100644 (file)
@@ -1,15 +1,18 @@
 use crate::consts::{constant, Constant};
-use crate::utils::{match_trait_method, paths, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::source::snippet_with_applicability;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
+use rustc_span::sym;
 
 use super::ITER_NTH_ZERO;
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, nth_args: &'tcx [hir::Expr<'_>]) {
     if_chain! {
-        if match_trait_method(cx, expr, &paths::ITERATOR);
+        if is_trait_method(cx, expr, sym::Iterator);
         if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), &nth_args[1]);
         then {
             let mut applicability = Applicability::MachineApplicable;
index 5f5969134e49062483a4a5ab31f5934a28a02ff5..b1d398876d3a5ebda1c57599363bf5b048a0d4f9 100644 (file)
@@ -1,22 +1,24 @@
-use crate::utils::{match_trait_method, paths, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::source::snippet;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
+use rustc_span::sym;
 
 use super::ITER_SKIP_NEXT;
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, skip_args: &[hir::Expr<'_>]) {
     // lint if caller of skip is an Iterator
-    if match_trait_method(cx, expr, &paths::ITERATOR) {
+    if is_trait_method(cx, expr, sym::Iterator) {
         if let [caller, n] = skip_args {
-            let hint = format!(".nth({})", snippet(cx, n.span, ".."));
             span_lint_and_sugg(
                 cx,
                 ITER_SKIP_NEXT,
                 expr.span.trim_start(caller.span).unwrap(),
                 "called `skip(..).next()` on an iterator",
                 "use `nth` instead",
-                hint,
+                format!(".nth({})", snippet(cx, n.span, "..")),
                 Applicability::MachineApplicable,
             );
         }
index 3e05d7f76b75aa2272f7b237a5b082d71543be62..3baa580314f337d98d1e947ea7fba25bcd0989d1 100644 (file)
@@ -1,12 +1,14 @@
 use crate::consts::{constant, Constant};
-use crate::utils::{match_trait_method, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_trait_method;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
+use rustc_span::sym;
 
 use super::ITERATOR_STEP_BY_ZERO;
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, args: &'tcx [hir::Expr<'_>]) {
-    if match_trait_method(cx, expr, &paths::ITERATOR) {
+    if is_trait_method(cx, expr, sym::Iterator) {
         if let Some((Constant::Int(0), _)) = constant(cx, cx.typeck_results(), &args[1]) {
             span_lint(
                 cx,
index 0b414e0eb95676e46e9037db66be59d73bdeebac..f16699322d13d7ba104f2710706f9ce94bfabb91 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{match_qpath, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::match_qpath;
+use clippy_utils::source::snippet_with_applicability;
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_errors::Applicability;
index 5b20e268d9f7ee8d7060d7e7eadd0fc52bbbbb73..e4402b2da21752eba48d5fb0aa5723c0fc674286 100644 (file)
@@ -1,4 +1,7 @@
-use crate::utils::{is_type_diagnostic_item, match_trait_method, paths, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -17,7 +20,7 @@ pub(super) fn check(
     if_chain! {
         // called on Iterator
         if let [map_expr] = collect_args;
-        if match_trait_method(cx, map_expr, &paths::ITERATOR);
+        if is_trait_method(cx, map_expr, sym::Iterator);
         // return of collect `Result<(),_>`
         let collect_ret_ty = cx.typeck_results().expr_ty(expr);
         if is_type_diagnostic_item(cx, collect_ret_ty, sym::result_type);
index 14a14e4f9ecd1a4d24aae583077810b31b2731c8..4bc52b036a8d41e797a851720d023c6827aba92c 100644 (file)
@@ -1,4 +1,7 @@
-use crate::utils::{is_type_diagnostic_item, match_trait_method, paths, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_trait_method;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -10,7 +13,7 @@
 /// lint use of `map().flatten()` for `Iterators` and 'Options'
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, map_args: &'tcx [hir::Expr<'_>]) {
     // lint if caller of `.map().flatten()` is an Iterator
-    if match_trait_method(cx, expr, &paths::ITERATOR) {
+    if is_trait_method(cx, expr, sym::Iterator) {
         let map_closure_ty = cx.typeck_results().expr_ty(&map_args[1]);
         let is_map_to_option = match map_closure_ty.kind() {
             ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
index 63b2cf87f32ada410984e722fcd10bb817ea259f..deb4b4492b5d104461be56a42312a64f74bfba6e 100644 (file)
@@ -1,5 +1,8 @@
-use crate::utils::usage::mutated_variables;
-use crate::utils::{is_type_diagnostic_item, meets_msrv, snippet, span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::meets_msrv;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::usage::mutated_variables;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
index 7fd14c4f9b11c055a44dcc43ec416977ee6ba891..fccdee078778e523e3ce3af6fa78dca9b366a46f 100644 (file)
@@ -1,5 +1,11 @@
 mod bind_instead_of_map;
 mod bytes_nth;
+mod chars_cmp;
+mod chars_cmp_with_unwrap;
+mod chars_last_cmp;
+mod chars_last_cmp_with_unwrap;
+mod chars_next_cmp;
+mod chars_next_cmp_with_unwrap;
 mod clone_on_copy;
 mod clone_on_ref_ptr;
 mod expect_fun_call;
@@ -36,6 +42,7 @@
 mod option_map_unwrap_or;
 mod or_fun_call;
 mod search_is_some;
+mod single_char_add_str;
 mod single_char_insert_string;
 mod single_char_pattern;
 mod single_char_push_string;
 mod unnecessary_lazy_eval;
 mod unwrap_used;
 mod useless_asref;
+mod utils;
 mod wrong_self_convention;
 mod zst_offset;
 
 use bind_instead_of_map::BindInsteadOfMap;
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::ty::{contains_adt_constructor, contains_ty, implements_trait, is_copy, is_type_diagnostic_item};
+use clippy_utils::{contains_return, get_trait_def_id, in_macro, iter_input_pats, method_calls, paths, return_ty};
 use if_chain::if_chain;
-use rustc_ast::ast;
-use rustc_errors::Applicability;
 use rustc_hir as hir;
-use rustc_hir::{TraitItem, TraitItemKind};
-use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
+use rustc_hir::def::Res;
+use rustc_hir::{PrimTy, QPath, TraitItem, TraitItemKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, TraitRef, Ty, TyS};
 use rustc_semver::RustcVersion;
 use rustc_span::symbol::{sym, SymbolStr};
 use rustc_typeck::hir_ty_to_ty;
 
-use crate::utils::{
-    contains_return, contains_ty, get_trait_def_id, implements_trait, in_macro, is_copy, is_type_diagnostic_item,
-    iter_input_pats, match_def_path, match_qpath, method_calls, method_chain_args, paths, return_ty,
-    single_segment_path, snippet_with_applicability, span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
-};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for `.unwrap()` calls on `Option`s and on `Result`s.
     ///
     /// **What it does:** Checks for methods with certain name prefixes and which
     /// doesn't match how self is taken. The actual rules are:
     ///
-    /// |Prefix |`self` taken          |
-    /// |-------|----------------------|
-    /// |`as_`  |`&self` or `&mut self`|
-    /// |`from_`| none                 |
-    /// |`into_`|`self`                |
-    /// |`is_`  |`&self` or none       |
-    /// |`to_`  |`&self`               |
+    /// |Prefix |Postfix     |`self` taken           | `self` type  |
+    /// |-------|------------|-----------------------|--------------|
+    /// |`as_`  | none       |`&self` or `&mut self` | any          |
+    /// |`from_`| none       | none                  | any          |
+    /// |`into_`| none       |`self`                 | any          |
+    /// |`is_`  | none       |`&self` or none        | any          |
+    /// |`to_`  | `_mut`     |`&mut self`            | any          |
+    /// |`to_`  | not `_mut` |`self`                 | `Copy`       |
+    /// |`to_`  | not `_mut` |`&self`                | not `Copy`   |
+    ///
+    /// Please find more info here:
+    /// https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
     ///
     /// **Why is this bad?** Consistency breeds readability. If you follow the
     /// conventions, your users won't be surprised that they, e.g., need to supply a
 
 declare_clippy_lint! {
     /// **What it does:** Checks for an iterator or string search (such as `find()`,
-    /// `position()`, or `rposition()`) followed by a call to `is_some()`.
+    /// `position()`, or `rposition()`) followed by a call to `is_some()` or `is_none()`.
     ///
-    /// **Why is this bad?** Readability, this can be written more concisely as
-    /// `_.any(_)` or `_.contains(_)`.
+    /// **Why is this bad?** Readability, this can be written more concisely as:
+    /// * `_.any(_)`, or `_.contains(_)` for `is_some()`,
+    /// * `!_.any(_)`, or `!_.contains(_)` for `is_none()`.
     ///
     /// **Known problems:** None.
     ///
     /// **Example:**
     /// ```rust
-    /// let vec = vec![1];
+    /// let vec = vec![1];
     /// vec.iter().find(|x| **x == 0).is_some();
+    ///
+    /// let _ = "hello world".find("world").is_none();
     /// ```
     /// Could be written as
     /// ```rust
-    /// let vec = vec![1];
+    /// let vec = vec![1];
     /// vec.iter().any(|x| *x == 0);
+    ///
+    /// let _ = !"hello world".contains("world");
     /// ```
     pub SEARCH_IS_SOME,
     complexity,
-    "using an iterator or string search followed by `is_some()`, which is more succinctly expressed as a call to `any()` or `contains()`"
+    "using an iterator or string search followed by `is_some()` or `is_none()`, which is more succinctly expressed as a call to `any()` or `contains()` (with negation in case of `is_none()`)"
 }
 
 declare_clippy_lint! {
@@ -1707,19 +1721,49 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
             ["next", "skip_while"] => skip_while_next::check(cx, expr, arg_lists[1]),
             ["next", "iter"] => iter_next_slice::check(cx, expr, arg_lists[1]),
             ["map", "filter"] => filter_map::check(cx, expr, false),
-            ["map", "filter_map"] => filter_map_map::check(cx, expr, arg_lists[1], arg_lists[0]),
+            ["map", "filter_map"] => filter_map_map::check(cx, expr),
             ["next", "filter_map"] => filter_map_next::check(cx, expr, arg_lists[1], self.msrv.as_ref()),
             ["map", "find"] => filter_map::check(cx, expr, true),
-            ["flat_map", "filter"] => filter_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]),
-            ["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr, arg_lists[1], arg_lists[0]),
+            ["flat_map", "filter"] => filter_flat_map::check(cx, expr),
+            ["flat_map", "filter_map"] => filter_map_flat_map::check(cx, expr),
             ["flat_map", ..] => flat_map_identity::check(cx, expr, arg_lists[0], method_spans[0]),
             ["flatten", "map"] => map_flatten::check(cx, expr, arg_lists[1]),
-            ["is_some", "find"] => search_is_some::check(cx, expr, "find", arg_lists[1], arg_lists[0], method_spans[1]),
-            ["is_some", "position"] => {
-                search_is_some::check(cx, expr, "position", arg_lists[1], arg_lists[0], method_spans[1])
+            [option_check_method, "find"] if "is_some" == *option_check_method || "is_none" == *option_check_method => {
+                search_is_some::check(
+                    cx,
+                    expr,
+                    "find",
+                    option_check_method,
+                    arg_lists[1],
+                    arg_lists[0],
+                    method_spans[1],
+                )
             },
-            ["is_some", "rposition"] => {
-                search_is_some::check(cx, expr, "rposition", arg_lists[1], arg_lists[0], method_spans[1])
+            [option_check_method, "position"]
+                if "is_some" == *option_check_method || "is_none" == *option_check_method =>
+            {
+                search_is_some::check(
+                    cx,
+                    expr,
+                    "position",
+                    option_check_method,
+                    arg_lists[1],
+                    arg_lists[0],
+                    method_spans[1],
+                )
+            },
+            [option_check_method, "rposition"]
+                if "is_some" == *option_check_method || "is_none" == *option_check_method =>
+            {
+                search_is_some::check(
+                    cx,
+                    expr,
+                    "rposition",
+                    option_check_method,
+                    arg_lists[1],
+                    arg_lists[0],
+                    method_spans[1],
+                )
             },
             ["extend", ..] => string_extend_chars::check(cx, expr, arg_lists[0]),
             ["count", "into_iter"] => iter_count::check(cx, expr, &arg_lists[1], "into_iter"),
@@ -1739,7 +1783,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
                 unnecessary_filter_map::check(cx, expr, arg_lists[0]);
                 filter_map_identity::check(cx, expr, arg_lists[0], method_spans[0]);
             },
-            ["count", "map"] => suspicious_map::check(cx, expr),
+            ["count", "map"] => suspicious_map::check(cx, expr, arg_lists[1], arg_lists[0]),
             ["assume_init"] => uninit_assumed_init::check(cx, &arg_lists[0][0], expr),
             ["unwrap_or", arith @ ("checked_add" | "checked_sub" | "checked_mul")] => {
                 manual_saturating_arithmetic::check(cx, expr, &arg_lists, &arith["checked_".len()..])
@@ -1768,46 +1812,17 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
 
         match expr.kind {
             hir::ExprKind::Call(ref func, ref args) => {
-                if let hir::ExprKind::Path(path) = &func.kind {
-                    if match_qpath(path, &["from_iter"]) {
-                        from_iter_instead_of_collect::check(cx, expr, args);
-                    }
-                }
+                from_iter_instead_of_collect::check(cx, expr, args, &func.kind);
             },
             hir::ExprKind::MethodCall(ref method_call, ref method_span, ref args, _) => {
                 or_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
                 expect_fun_call::check(cx, expr, *method_span, &method_call.ident.as_str(), args);
-
-                let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0]);
-                if args.len() == 1 && method_call.ident.name == sym::clone {
-                    clone_on_copy::check(cx, expr, &args[0], self_ty);
-                    clone_on_ref_ptr::check(cx, expr, &args[0]);
-                }
-                if args.len() == 1 && method_call.ident.name == sym!(to_string) {
-                    inefficient_to_string::check(cx, expr, &args[0], self_ty);
-                }
-
-                if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
-                    if match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
-                        single_char_push_string::check(cx, expr, args);
-                    } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) {
-                        single_char_insert_string::check(cx, expr, args);
-                    }
-                }
-
-                match self_ty.kind() {
-                    ty::Ref(_, ty, _) if *ty.kind() == ty::Str => {
-                        for &(method, pos) in &PATTERN_METHODS {
-                            if method_call.ident.name.as_str() == method && args.len() > pos {
-                                single_char_pattern::check(cx, expr, &args[pos]);
-                            }
-                        }
-                    },
-                    ty::Ref(..) if method_call.ident.name == sym::into_iter => {
-                        into_iter_on_ref::check(cx, expr, self_ty, *method_span);
-                    },
-                    _ => (),
-                }
+                clone_on_copy::check(cx, expr, method_call.ident.name, args);
+                clone_on_ref_ptr::check(cx, expr, method_call.ident.name, args);
+                inefficient_to_string::check(cx, expr, method_call.ident.name, args);
+                single_char_add_str::check(cx, expr, args);
+                into_iter_on_ref::check(cx, expr, *method_span, method_call.ident.name, args);
+                single_char_pattern::check(cx, expr, method_call.ident.name, args);
             },
             hir::ExprKind::Binary(op, ref lhs, ref rhs)
                 if op.node == hir::BinOpKind::Eq || op.node == hir::BinOpKind::Ne =>
@@ -1834,10 +1849,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
         let item = cx.tcx.hir().expect_item(parent);
         let self_ty = cx.tcx.type_of(item.def_id);
 
-        // if this impl block implements a trait, lint in trait definition instead
-        if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = item.kind {
-            return;
-        }
+        let implements_trait = matches!(item.kind, hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }));
 
         if_chain! {
             if let hir::ImplItemKind::Fn(ref sig, id) = impl_item.kind;
@@ -1852,12 +1864,13 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
             if let Some(first_arg_ty) = first_arg_ty;
 
             then {
-                if cx.access_levels.is_exported(impl_item.hir_id()) {
+                // if this impl block implements a trait, lint in trait definition instead
+                if !implements_trait && cx.access_levels.is_exported(impl_item.hir_id()) {
                     // check missing trait implementations
                     for method_config in &TRAIT_METHODS {
                         if name == method_config.method_name &&
                             sig.decl.inputs.len() == method_config.param_count &&
-                            method_config.output_type.matches(cx, &sig.decl.output) &&
+                            method_config.output_type.matches(&sig.decl.output) &&
                             method_config.self_kind.matches(cx, self_ty, first_arg_ty) &&
                             fn_header_equals(method_config.fn_header, sig.header) &&
                             method_config.lifetime_param_cond(&impl_item)
@@ -1888,16 +1901,26 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
                     item.vis.node.is_pub(),
                     self_ty,
                     first_arg_ty,
-                    first_arg.pat.span
+                    first_arg.pat.span,
+                    false
                 );
             }
         }
 
+        // if this impl block implements a trait, lint in trait definition instead
+        if implements_trait {
+            return;
+        }
+
         if let hir::ImplItemKind::Fn(_, _) = impl_item.kind {
             let ret_ty = return_ty(cx, impl_item.hir_id());
 
             // walk the return type and check for Self (this does not check associated types)
-            if contains_ty(ret_ty, self_ty) {
+            if let Some(self_adt) = self_ty.ty_adt_def() {
+                if contains_adt_constructor(ret_ty, self_adt) {
+                    return;
+                }
+            } else if contains_ty(ret_ty, self_ty) {
                 return;
             }
 
@@ -1907,7 +1930,11 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
                 for &(predicate, _span) in cx.tcx.explicit_item_bounds(def_id) {
                     if let ty::PredicateKind::Projection(projection_predicate) = predicate.kind().skip_binder() {
                         // walk the associated type and check for Self
-                        if contains_ty(projection_predicate.ty, self_ty) {
+                        if let Some(self_adt) = self_ty.ty_adt_def() {
+                            if contains_adt_constructor(projection_predicate.ty, self_adt) {
+                                return;
+                            }
+                        } else if contains_ty(projection_predicate.ty, self_ty) {
                             return;
                         }
                     }
@@ -1944,7 +1971,8 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>
                     false,
                     self_ty,
                     first_arg_ty,
-                    first_arg_span
+                    first_arg_span,
+                    true
                 );
             }
         }
@@ -1969,47 +1997,6 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>
 
     extract_msrv_attr!(LateContext);
 }
-
-fn derefs_to_slice<'tcx>(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx hir::Expr<'tcx>,
-    ty: Ty<'tcx>,
-) -> Option<&'tcx hir::Expr<'tcx>> {
-    fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool {
-        match ty.kind() {
-            ty::Slice(_) => true,
-            ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
-            ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type),
-            ty::Array(_, size) => size
-                .try_eval_usize(cx.tcx, cx.param_env)
-                .map_or(false, |size| size < 32),
-            ty::Ref(_, inner, _) => may_slice(cx, inner),
-            _ => false,
-        }
-    }
-
-    if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind {
-        if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) {
-            Some(&args[0])
-        } else {
-            None
-        }
-    } else {
-        match ty.kind() {
-            ty::Slice(_) => Some(expr),
-            ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
-            ty::Ref(_, inner, _) => {
-                if may_slice(cx, inner) {
-                    Some(expr)
-                } else {
-                    None
-                }
-            },
-            _ => None,
-        }
-    }
-}
-
 /// Used for `lint_binary_expr_with_method_call`.
 #[derive(Copy, Clone)]
 struct BinaryExprInfo<'a> {
@@ -2022,7 +2009,7 @@ struct BinaryExprInfo<'a> {
 /// Checks for the `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
 fn lint_binary_expr_with_method_call(cx: &LateContext<'_>, info: &mut BinaryExprInfo<'_>) {
     macro_rules! lint_with_both_lhs_and_rhs {
-        ($func:ident, $cx:expr, $info:ident) => {
+        ($func:expr, $cx:expr, $info:ident) => {
             if !$func($cx, $info) {
                 ::std::mem::swap(&mut $info.chain, &mut $info.other);
                 if $func($cx, $info) {
@@ -2032,145 +2019,10 @@ macro_rules! lint_with_both_lhs_and_rhs {
         };
     }
 
-    lint_with_both_lhs_and_rhs!(lint_chars_next_cmp, cx, info);
-    lint_with_both_lhs_and_rhs!(lint_chars_last_cmp, cx, info);
-    lint_with_both_lhs_and_rhs!(lint_chars_next_cmp_with_unwrap, cx, info);
-    lint_with_both_lhs_and_rhs!(lint_chars_last_cmp_with_unwrap, cx, info);
-}
-
-/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
-fn lint_chars_cmp(
-    cx: &LateContext<'_>,
-    info: &BinaryExprInfo<'_>,
-    chain_methods: &[&str],
-    lint: &'static Lint,
-    suggest: &str,
-) -> bool {
-    if_chain! {
-        if let Some(args) = method_chain_args(info.chain, chain_methods);
-        if let hir::ExprKind::Call(ref fun, ref arg_char) = info.other.kind;
-        if arg_char.len() == 1;
-        if let hir::ExprKind::Path(ref qpath) = fun.kind;
-        if let Some(segment) = single_segment_path(qpath);
-        if segment.ident.name == sym::Some;
-        then {
-            let mut applicability = Applicability::MachineApplicable;
-            let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
-
-            if *self_ty.kind() != ty::Str {
-                return false;
-            }
-
-            span_lint_and_sugg(
-                cx,
-                lint,
-                info.expr.span,
-                &format!("you should use the `{}` method", suggest),
-                "like this",
-                format!("{}{}.{}({})",
-                        if info.eq { "" } else { "!" },
-                        snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
-                        suggest,
-                        snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)),
-                applicability,
-            );
-
-            return true;
-        }
-    }
-
-    false
-}
-
-/// Checks for the `CHARS_NEXT_CMP` lint.
-fn lint_chars_next_cmp<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool {
-    lint_chars_cmp(cx, info, &["chars", "next"], CHARS_NEXT_CMP, "starts_with")
-}
-
-/// Checks for the `CHARS_LAST_CMP` lint.
-fn lint_chars_last_cmp<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool {
-    if lint_chars_cmp(cx, info, &["chars", "last"], CHARS_LAST_CMP, "ends_with") {
-        true
-    } else {
-        lint_chars_cmp(cx, info, &["chars", "next_back"], CHARS_LAST_CMP, "ends_with")
-    }
-}
-
-/// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints with `unwrap()`.
-fn lint_chars_cmp_with_unwrap<'tcx>(
-    cx: &LateContext<'tcx>,
-    info: &BinaryExprInfo<'_>,
-    chain_methods: &[&str],
-    lint: &'static Lint,
-    suggest: &str,
-) -> bool {
-    if_chain! {
-        if let Some(args) = method_chain_args(info.chain, chain_methods);
-        if let hir::ExprKind::Lit(ref lit) = info.other.kind;
-        if let ast::LitKind::Char(c) = lit.node;
-        then {
-            let mut applicability = Applicability::MachineApplicable;
-            span_lint_and_sugg(
-                cx,
-                lint,
-                info.expr.span,
-                &format!("you should use the `{}` method", suggest),
-                "like this",
-                format!("{}{}.{}('{}')",
-                        if info.eq { "" } else { "!" },
-                        snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
-                        suggest,
-                        c),
-                applicability,
-            );
-
-            true
-        } else {
-            false
-        }
-    }
-}
-
-/// Checks for the `CHARS_NEXT_CMP` lint with `unwrap()`.
-fn lint_chars_next_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool {
-    lint_chars_cmp_with_unwrap(cx, info, &["chars", "next", "unwrap"], CHARS_NEXT_CMP, "starts_with")
-}
-
-/// Checks for the `CHARS_LAST_CMP` lint with `unwrap()`.
-fn lint_chars_last_cmp_with_unwrap<'tcx>(cx: &LateContext<'tcx>, info: &BinaryExprInfo<'_>) -> bool {
-    if lint_chars_cmp_with_unwrap(cx, info, &["chars", "last", "unwrap"], CHARS_LAST_CMP, "ends_with") {
-        true
-    } else {
-        lint_chars_cmp_with_unwrap(cx, info, &["chars", "next_back", "unwrap"], CHARS_LAST_CMP, "ends_with")
-    }
-}
-
-fn get_hint_if_single_char_arg(
-    cx: &LateContext<'_>,
-    arg: &hir::Expr<'_>,
-    applicability: &mut Applicability,
-) -> Option<String> {
-    if_chain! {
-        if let hir::ExprKind::Lit(lit) = &arg.kind;
-        if let ast::LitKind::Str(r, style) = lit.node;
-        let string = r.as_str();
-        if string.chars().count() == 1;
-        then {
-            let snip = snippet_with_applicability(cx, arg.span, &string, applicability);
-            let ch = if let ast::StrStyle::Raw(nhash) = style {
-                let nhash = nhash as usize;
-                // for raw string: r##"a"##
-                &snip[(nhash + 2)..(snip.len() - 1 - nhash)]
-            } else {
-                // for regular string: "a"
-                &snip[1..(snip.len() - 1)]
-            };
-            let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
-            Some(hint)
-        } else {
-            None
-        }
-    }
+    lint_with_both_lhs_and_rhs!(chars_next_cmp::check, cx, info);
+    lint_with_both_lhs_and_rhs!(chars_last_cmp::check, cx, info);
+    lint_with_both_lhs_and_rhs!(chars_next_cmp_with_unwrap::check, cx, info);
+    lint_with_both_lhs_and_rhs!(chars_last_cmp_with_unwrap::check, cx, info);
 }
 
 const FN_HEADER: hir::FnHeader = hir::FnHeader {
@@ -2336,10 +2188,10 @@ fn matches_ref<'a>(cx: &LateContext<'a>, mutability: hir::Mutability, parent_ty:
     #[must_use]
     fn description(self) -> &'static str {
         match self {
-            Self::Value => "self by value",
-            Self::Ref => "self by reference",
-            Self::RefMut => "self by mutable reference",
-            Self::No => "no self",
+            Self::Value => "`self` by value",
+            Self::Ref => "`self` by reference",
+            Self::RefMut => "`self` by mutable reference",
+            Self::No => "no `self`",
         }
     }
 }
@@ -2353,8 +2205,8 @@ enum OutType {
 }
 
 impl OutType {
-    fn matches(self, cx: &LateContext<'_>, ty: &hir::FnRetTy<'_>) -> bool {
-        let is_unit = |ty: &hir::Ty<'_>| SpanlessEq::new(cx).eq_ty_kind(&ty.kind, &hir::TyKind::Tup(&[]));
+    fn matches(self, ty: &hir::FnRetTy<'_>) -> bool {
+        let is_unit = |ty: &hir::Ty<'_>| matches!(ty.kind, hir::TyKind::Tup(&[]));
         match (self, ty) {
             (Self::Unit, &hir::FnRetTy::DefaultReturn(_)) => true,
             (Self::Unit, &hir::FnRetTy::Return(ref ty)) if is_unit(ty) => true,
@@ -2367,8 +2219,8 @@ fn matches(self, cx: &LateContext<'_>, ty: &hir::FnRetTy<'_>) -> bool {
 }
 
 fn is_bool(ty: &hir::Ty<'_>) -> bool {
-    if let hir::TyKind::Path(ref p) = ty.kind {
-        match_qpath(p, &["bool"])
+    if let hir::TyKind::Path(QPath::Resolved(_, path)) = ty.kind {
+        matches!(path.res, Res::PrimTy(PrimTy::Bool))
     } else {
         false
     }
index c1706cc7cc7d266ad0c2f902e3a2d615148c6ca0..e6ce9cac39729ec8383052f2fb0572ee027eb459 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{implements_trait, is_type_diagnostic_item, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
index 89067dbfe0e51a780e6e6df125d2c9cfad98e357..d11ede080dc8d8aac522a6c5a3a4d568d324e2ca 100644 (file)
@@ -1,7 +1,7 @@
-use crate::utils::{
-    is_type_diagnostic_item, match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks, snippet,
-    span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{match_def_path, meets_msrv, path_to_local_id, paths, remove_blocks};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
index 64f6ebc5062ef3ef208c2190f7b50891702ffaaf..d93db2c22e4c98f5b2f6ff81a27e2f2baaa54c85 100644 (file)
@@ -1,4 +1,7 @@
-use crate::utils::{is_type_diagnostic_item, match_qpath, paths, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{match_qpath, paths};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
index 7cdd49bbf0307a6a3a2adaa6c6e8b1e976f1ce55..e252abc177a2e6993fd45582ef4ce42f68d1061d 100644 (file)
@@ -1,5 +1,8 @@
-use crate::utils::{differing_macro_contexts, snippet_with_applicability, span_lint_and_then};
-use crate::utils::{is_copy, is_type_diagnostic_item};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::differing_macro_contexts;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_copy;
+use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_path, NestedVisitorMap, Visitor};
index 5f7fc431d22483edca2dbf1c8ce14b0021bf60c5..89dedc5f0d80715d1d5db376d78fcd869e8f7067 100644 (file)
@@ -1,11 +1,12 @@
-use crate::utils::eager_or_lazy::is_lazyness_candidate;
-use crate::utils::{
-    contains_return, get_trait_def_id, implements_trait, is_type_diagnostic_item, last_path_segment, match_type, paths,
-    snippet, snippet_with_applicability, snippet_with_macro_callsite, span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::eager_or_lazy::is_lazyness_candidate;
+use clippy_utils::source::{snippet, snippet_with_applicability, snippet_with_macro_callsite};
+use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type};
+use clippy_utils::{contains_return, get_trait_def_id, last_path_segment, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
+use rustc_hir::{BlockCheckMode, UnsafeSource};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_span::source_map::Span;
@@ -90,7 +91,7 @@ fn check_general_case<'tcx>(
                 let ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
 
                 match ty.kind() {
-                    ty::Slice(_) | ty::Array(_, _) => return,
+                    ty::Slice(_) | ty::Array(_, _) | ty::Str => return,
                     _ => (),
                 }
 
@@ -167,7 +168,16 @@ fn check_general_case<'tcx>(
             hir::ExprKind::Index(..) | hir::ExprKind::MethodCall(..) => {
                 check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
             },
-            _ => {},
+            hir::ExprKind::Block(block, _) => {
+                if let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules {
+                    if let Some(block_expr) = block.expr {
+                        if let hir::ExprKind::MethodCall(..) = block_expr.kind {
+                            check_general_case(cx, name, method_span, &args[0], &args[1], expr.span, None);
+                        }
+                    }
+                }
+            },
+            _ => (),
         }
     }
 }
index e9e654432208d94813f5083cfb2fe931faa47829..de7d168295fb90907a8e1af1d1dd6cdb1271fd45 100644 (file)
@@ -1,7 +1,7 @@
-use crate::utils::{
-    is_type_diagnostic_item, match_trait_method, paths, snippet, snippet_with_applicability, span_lint_and_help,
-    span_lint_and_sugg, strip_pat_refs,
-};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{is_trait_method, strip_pat_refs};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use super::SEARCH_IS_SOME;
 
 /// lint searching an Iterator followed by `is_some()`
-/// or calling `find()` on a string followed by `is_some()`
+/// or calling `find()` on a string followed by `is_some()` or `is_none()`
+#[allow(clippy::too_many_lines)]
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx hir::Expr<'_>,
     search_method: &str,
+    option_check_method: &str,
     search_args: &'tcx [hir::Expr<'_>],
     is_some_args: &'tcx [hir::Expr<'_>],
     method_span: Span,
 ) {
     // lint if caller of search is an Iterator
-    if match_trait_method(cx, &is_some_args[0], &paths::ITERATOR) {
+    if is_trait_method(cx, &is_some_args[0], sym::Iterator) {
         let msg = format!(
-            "called `is_some()` after searching an `Iterator` with `{}`",
-            search_method
+            "called `{}()` after searching an `Iterator` with `{}`",
+            option_check_method, search_method
         );
-        let hint = "this is more succinctly expressed by calling `any()`";
         let search_snippet = snippet(cx, search_args[1].span, "..");
         if search_snippet.lines().count() <= 1 {
             // suggest `any(|x| ..)` instead of `any(|&x| ..)` for `find(|&x| ..).is_some()`
@@ -53,20 +54,49 @@ pub(super) fn check<'tcx>(
                 }
             };
             // add note if not multi-line
-            span_lint_and_sugg(
-                cx,
-                SEARCH_IS_SOME,
-                method_span.with_hi(expr.span.hi()),
-                &msg,
-                "use `any()` instead",
-                format!(
-                    "any({})",
-                    any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
-                ),
-                Applicability::MachineApplicable,
-            );
+            match option_check_method {
+                "is_some" => {
+                    span_lint_and_sugg(
+                        cx,
+                        SEARCH_IS_SOME,
+                        method_span.with_hi(expr.span.hi()),
+                        &msg,
+                        "use `any()` instead",
+                        format!(
+                            "any({})",
+                            any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
+                        ),
+                        Applicability::MachineApplicable,
+                    );
+                },
+                "is_none" => {
+                    let iter = snippet(cx, search_args[0].span, "..");
+                    span_lint_and_sugg(
+                        cx,
+                        SEARCH_IS_SOME,
+                        expr.span,
+                        &msg,
+                        "use `!_.any()` instead",
+                        format!(
+                            "!{}.any({})",
+                            iter,
+                            any_search_snippet.as_ref().map_or(&*search_snippet, String::as_str)
+                        ),
+                        Applicability::MachineApplicable,
+                    );
+                },
+                _ => (),
+            }
         } else {
-            span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, hint);
+            let hint = format!(
+                "this is more succinctly expressed by calling `any()`{}",
+                if option_check_method == "is_none" {
+                    " with negation"
+                } else {
+                    ""
+                }
+            );
+            span_lint_and_help(cx, SEARCH_IS_SOME, expr.span, &msg, None, &hint);
         }
     }
     // lint if `find()` is called by `String` or `&str`
@@ -83,18 +113,37 @@ pub(super) fn check<'tcx>(
             if is_string_or_str_slice(&search_args[0]);
             if is_string_or_str_slice(&search_args[1]);
             then {
-                let msg = "called `is_some()` after calling `find()` on a string";
-                let mut applicability = Applicability::MachineApplicable;
-                let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability);
-                span_lint_and_sugg(
-                    cx,
-                    SEARCH_IS_SOME,
-                    method_span.with_hi(expr.span.hi()),
-                    msg,
-                    "use `contains()` instead",
-                    format!("contains({})", find_arg),
-                    applicability,
-                );
+                let msg = format!("called `{}()` after calling `find()` on a string", option_check_method);
+                match option_check_method {
+                    "is_some" => {
+                        let mut applicability = Applicability::MachineApplicable;
+                        let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability);
+                        span_lint_and_sugg(
+                            cx,
+                            SEARCH_IS_SOME,
+                            method_span.with_hi(expr.span.hi()),
+                            &msg,
+                            "use `contains()` instead",
+                            format!("contains({})", find_arg),
+                            applicability,
+                        );
+                    },
+                    "is_none" => {
+                        let string = snippet(cx, search_args[0].span, "..");
+                        let mut applicability = Applicability::MachineApplicable;
+                        let find_arg = snippet_with_applicability(cx, search_args[1].span, "..", &mut applicability);
+                        span_lint_and_sugg(
+                            cx,
+                            SEARCH_IS_SOME,
+                            expr.span,
+                            &msg,
+                            "use `!_.contains()` instead",
+                            format!("!{}.contains({})", string, find_arg),
+                            applicability,
+                        );
+                    },
+                    _ => (),
+                }
             }
         }
     }
diff --git a/clippy_lints/src/methods/single_char_add_str.rs b/clippy_lints/src/methods/single_char_add_str.rs
new file mode 100644 (file)
index 0000000..9a5fabc
--- /dev/null
@@ -0,0 +1,14 @@
+use crate::methods::{single_char_insert_string, single_char_push_string};
+use clippy_utils::{match_def_path, paths};
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
+    if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
+        if match_def_path(cx, fn_def_id, &paths::PUSH_STR) {
+            single_char_push_string::check(cx, expr, args);
+        } else if match_def_path(cx, fn_def_id, &paths::INSERT_STR) {
+            single_char_insert_string::check(cx, expr, args);
+        }
+    }
+}
index 0ce8b66978dc12cdd4e307a8612ab2dce2d860cf..6cdc954c03be15ea38f0f15e587104e09ea61a78 100644 (file)
@@ -1,5 +1,6 @@
-use crate::methods::get_hint_if_single_char_arg;
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
+use super::utils::get_hint_if_single_char_arg;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
index 61cbc9d2f0a6296d83d1b58af359380e9fdedd5e..f4090c7c617d4499c07cc79b6c3bcd5030f43ef4 100644 (file)
@@ -1,23 +1,35 @@
-use crate::methods::get_hint_if_single_char_arg;
-use crate::utils::span_lint_and_sugg;
+use super::utils::get_hint_if_single_char_arg;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::symbol::Symbol;
 
 use super::SINGLE_CHAR_PATTERN;
 
 /// lint for length-1 `str`s for methods in `PATTERN_METHODS`
-pub(super) fn check(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, arg: &hir::Expr<'_>) {
-    let mut applicability = Applicability::MachineApplicable;
-    if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability) {
-        span_lint_and_sugg(
-            cx,
-            SINGLE_CHAR_PATTERN,
-            arg.span,
-            "single-character string constant used as pattern",
-            "try using a `char` instead",
-            hint,
-            applicability,
-        );
+pub(super) fn check(cx: &LateContext<'_>, _expr: &hir::Expr<'_>, method_name: Symbol, args: &[hir::Expr<'_>]) {
+    for &(method, pos) in &crate::methods::PATTERN_METHODS {
+        if_chain! {
+            if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(&args[0]).kind();
+            if *ty.kind() == ty::Str;
+            if method_name.as_str() == method && args.len() > pos;
+            let arg = &args[pos];
+            let mut applicability = Applicability::MachineApplicable;
+            if let Some(hint) = get_hint_if_single_char_arg(cx, arg, &mut applicability);
+            then {
+                span_lint_and_sugg(
+                    cx,
+                    SINGLE_CHAR_PATTERN,
+                    arg.span,
+                    "single-character string constant used as pattern",
+                    "try using a `char` instead",
+                    hint,
+                    applicability,
+                );
+            }
+        }
     }
 }
index deacc70b713e5ae15d6b571cb9877f8aa9a8fd10..0237d39cbdb4e767eecba43e99d478dfffd021bb 100644 (file)
@@ -1,5 +1,6 @@
-use crate::methods::get_hint_if_single_char_arg;
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
+use super::utils::get_hint_if_single_char_arg;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
index 8ba6ae952003e6ce480a65e9e316cd7dadf5a371..3db83785b591e21c012cc355f9ffb21ea3cba161 100644 (file)
@@ -1,13 +1,15 @@
-use crate::utils::{match_trait_method, paths, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_trait_method;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
+use rustc_span::sym;
 
 use super::SKIP_WHILE_NEXT;
 
 /// lint use of `skip_while().next()` for `Iterators`
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, _skip_while_args: &'tcx [hir::Expr<'_>]) {
     // lint if caller of `.skip_while().next()` is an Iterator
-    if match_trait_method(cx, expr, &paths::ITERATOR) {
+    if is_trait_method(cx, expr, sym::Iterator) {
         span_lint_and_help(
             cx,
             SKIP_WHILE_NEXT,
index 0a08ea26175fe17d7a2589923a4b303ae4abc9e8..5c688ac56211ea45d380206e8f62d7d3adf62272 100644 (file)
@@ -1,4 +1,7 @@
-use crate::utils::{is_type_diagnostic_item, method_chain_args, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::method_chain_args;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
     let obj_ty = cx.typeck_results().expr_ty(&args[0]).peel_refs();
-    if is_type_diagnostic_item(cx, obj_ty, sym::string_type) {
-        let arg = &args[1];
-        if let Some(arglists) = method_chain_args(arg, &["chars"]) {
-            let target = &arglists[0][0];
-            let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
-            let ref_str = if *self_ty.kind() == ty::Str {
-                ""
-            } else if is_type_diagnostic_item(cx, self_ty, sym::string_type) {
-                "&"
-            } else {
-                return;
-            };
+    if !is_type_diagnostic_item(cx, obj_ty, sym::string_type) {
+        return;
+    }
+    let arg = &args[1];
+    if let Some(arglists) = method_chain_args(arg, &["chars"]) {
+        let target = &arglists[0][0];
+        let self_ty = cx.typeck_results().expr_ty(target).peel_refs();
+        let ref_str = if *self_ty.kind() == ty::Str {
+            ""
+        } else if is_type_diagnostic_item(cx, self_ty, sym::string_type) {
+            "&"
+        } else {
+            return;
+        };
 
-            let mut applicability = Applicability::MachineApplicable;
-            span_lint_and_sugg(
-                cx,
-                STRING_EXTEND_CHARS,
-                expr.span,
-                "calling `.extend(_.chars())`",
-                "try this",
-                format!(
-                    "{}.push_str({}{})",
-                    snippet_with_applicability(cx, args[0].span, "..", &mut applicability),
-                    ref_str,
-                    snippet_with_applicability(cx, target.span, "..", &mut applicability)
-                ),
-                applicability,
-            );
-        }
+        let mut applicability = Applicability::MachineApplicable;
+        span_lint_and_sugg(
+            cx,
+            STRING_EXTEND_CHARS,
+            expr.span,
+            "calling `.extend(_.chars())`",
+            "try this",
+            format!(
+                "{}.push_str({}{})",
+                snippet_with_applicability(cx, args[0].span, "..", &mut applicability),
+                ref_str,
+                snippet_with_applicability(cx, target.span, "..", &mut applicability)
+            ),
+            applicability,
+        );
     }
 }
index e135a826dc4d093e855a228d679b40669249ceb9..7015bd54c35dd71fa823199e9697bf18908753a3 100644 (file)
@@ -1,16 +1,42 @@
-use crate::utils::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::usage::mutated_variables;
+use clippy_utils::{expr_or_init, is_trait_method};
+use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
+use rustc_span::sym;
 
 use super::SUSPICIOUS_MAP;
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>) {
-    span_lint_and_help(
-        cx,
-        SUSPICIOUS_MAP,
-        expr.span,
-        "this call to `map()` won't have an effect on the call to `count()`",
-        None,
-        "make sure you did not confuse `map` with `filter` or `for_each`",
-    );
+pub fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &hir::Expr<'_>,
+    map_args: &[hir::Expr<'_>],
+    count_args: &[hir::Expr<'_>],
+) {
+    if_chain! {
+        if let [count_recv] = count_args;
+        if let [_, map_arg] = map_args;
+        if is_trait_method(cx, count_recv, sym::Iterator);
+        let closure = expr_or_init(cx, map_arg);
+        if let Some(body_id) = cx.tcx.hir().maybe_body_owned_by(closure.hir_id);
+        let closure_body = cx.tcx.hir().body(body_id);
+        if !cx.typeck_results().expr_ty(&closure_body.value).is_unit();
+        then {
+            if let Some(map_mutated_vars) = mutated_variables(&closure_body.value, cx) {
+                // A variable is used mutably inside of the closure. Suppress the lint.
+                if !map_mutated_vars.is_empty() {
+                    return;
+                }
+            }
+            span_lint_and_help(
+                cx,
+                SUSPICIOUS_MAP,
+                expr.span,
+                "this call to `map()` won't have an effect on the call to `count()`",
+                None,
+                "make sure you did not confuse `map` with `filter` or `for_each`",
+            );
+        }
+    }
 }
index 798b66192c81829cd53afc1816f97277125ddc37..f2f6ef4be6cfd46d003f803fd91c9fbf0fd21f4f 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{match_def_path, match_qpath, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{match_def_path, match_qpath, paths};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
index 12b2cf0a16582f407d65902a5b1abe4fb64a189b..48d905ab8330a7fb9f8fb00df03b28b40b8e31b5 100644 (file)
@@ -1,16 +1,17 @@
-use crate::utils::usage::mutated_variables;
-use crate::utils::{match_qpath, match_trait_method, path_to_local_id, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::usage::mutated_variables;
+use clippy_utils::{is_trait_method, match_qpath, path_to_local_id, paths};
+use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
-
-use if_chain::if_chain;
+use rustc_span::sym;
 
 use super::UNNECESSARY_FILTER_MAP;
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
-    if !match_trait_method(cx, expr, &paths::ITERATOR) {
+    if !is_trait_method(cx, expr, sym::Iterator) {
         return;
     }
 
index a26443f4ee94420be3fb97007329249448c049e1..1268fd4bda99c6292405f1123fcbd342273d7bf2 100644 (file)
@@ -1,14 +1,13 @@
-use crate::utils::{
-    match_trait_method, path_to_local_id, paths, remove_blocks, snippet_with_applicability, span_lint_and_sugg,
-    strip_pat_refs,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{is_trait_method, path_to_local_id, remove_blocks, strip_pat_refs};
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::PatKind;
 use rustc_lint::LateContext;
-use rustc_span::source_map::Span;
+use rustc_span::{source_map::Span, sym};
 
 use super::UNNECESSARY_FOLD;
 
@@ -71,7 +70,7 @@ fn check_fold_with_op(
     }
 
     // Check that this is a call to Iterator::fold rather than just some function called fold
-    if !match_trait_method(cx, expr, &paths::ITERATOR) {
+    if !is_trait_method(cx, expr, sym::Iterator) {
         return;
     }
 
index a17259d697faa3128d33a83f8699f9e4f5efa86e..a86185bf0a6c3205e7fead0757c6e456b13fbdee 100644 (file)
@@ -1,5 +1,7 @@
-use crate::utils::{eager_or_lazy, usage};
-use crate::utils::{is_type_diagnostic_item, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{eager_or_lazy, usage};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
index 094c3fc45c493b097b13c4bba274938aa1c5f2ee..2f5806115bd5a55005c89a512f376f087a8c5c9c 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{is_type_diagnostic_item, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_span::sym;
index e4554f8d4897e21470d34605ec481ab7d4d34dec..b5505af0f7ee59181a33461113d48e6d766959c4 100644 (file)
@@ -1,6 +1,7 @@
-use crate::utils::{
-    get_parent_expr, match_trait_method, paths, snippet_with_applicability, span_lint_and_sugg, walk_ptrs_ty_depth,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::walk_ptrs_ty_depth;
+use clippy_utils::{get_parent_expr, match_trait_method, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
diff --git a/clippy_lints/src/methods/utils.rs b/clippy_lints/src/methods/utils.rs
new file mode 100644 (file)
index 0000000..ac6b553
--- /dev/null
@@ -0,0 +1,77 @@
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use if_chain::if_chain;
+use rustc_ast::ast;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, Ty};
+use rustc_span::symbol::sym;
+
+pub(super) fn derefs_to_slice<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'tcx>,
+    ty: Ty<'tcx>,
+) -> Option<&'tcx hir::Expr<'tcx>> {
+    fn may_slice<'a>(cx: &LateContext<'a>, ty: Ty<'a>) -> bool {
+        match ty.kind() {
+            ty::Slice(_) => true,
+            ty::Adt(def, _) if def.is_box() => may_slice(cx, ty.boxed_ty()),
+            ty::Adt(..) => is_type_diagnostic_item(cx, ty, sym::vec_type),
+            ty::Array(_, size) => size
+                .try_eval_usize(cx.tcx, cx.param_env)
+                .map_or(false, |size| size < 32),
+            ty::Ref(_, inner, _) => may_slice(cx, inner),
+            _ => false,
+        }
+    }
+
+    if let hir::ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind {
+        if path.ident.name == sym::iter && may_slice(cx, cx.typeck_results().expr_ty(&args[0])) {
+            Some(&args[0])
+        } else {
+            None
+        }
+    } else {
+        match ty.kind() {
+            ty::Slice(_) => Some(expr),
+            ty::Adt(def, _) if def.is_box() && may_slice(cx, ty.boxed_ty()) => Some(expr),
+            ty::Ref(_, inner, _) => {
+                if may_slice(cx, inner) {
+                    Some(expr)
+                } else {
+                    None
+                }
+            },
+            _ => None,
+        }
+    }
+}
+
+pub(super) fn get_hint_if_single_char_arg(
+    cx: &LateContext<'_>,
+    arg: &hir::Expr<'_>,
+    applicability: &mut Applicability,
+) -> Option<String> {
+    if_chain! {
+        if let hir::ExprKind::Lit(lit) = &arg.kind;
+        if let ast::LitKind::Str(r, style) = lit.node;
+        let string = r.as_str();
+        if string.chars().count() == 1;
+        then {
+            let snip = snippet_with_applicability(cx, arg.span, &string, applicability);
+            let ch = if let ast::StrStyle::Raw(nhash) = style {
+                let nhash = nhash as usize;
+                // for raw string: r##"a"##
+                &snip[(nhash + 2)..(snip.len() - 1 - nhash)]
+            } else {
+                // for regular string: "a"
+                &snip[1..(snip.len() - 1)]
+            };
+            let hint = format!("'{}'", if ch == "'" { "\\'" } else { ch });
+            Some(hint)
+        } else {
+            None
+        }
+    }
+}
index 90fab577436618a231b527c75b4e9fede9bc2413..59e683aa9a786fb18f732ad80ebffd9ab4e7aaa6 100644 (file)
@@ -1,5 +1,6 @@
 use crate::methods::SelfKind;
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_copy;
 use rustc_lint::LateContext;
 use rustc_middle::ty::TyS;
 use rustc_span::source_map::Span;
 use super::WRONG_SELF_CONVENTION;
 
 #[rustfmt::skip]
-const CONVENTIONS: [(Convention, &[SelfKind]); 7] = [
-    (Convention::Eq("new"), &[SelfKind::No]),
-    (Convention::StartsWith("as_"), &[SelfKind::Ref, SelfKind::RefMut]),
-    (Convention::StartsWith("from_"), &[SelfKind::No]),
-    (Convention::StartsWith("into_"), &[SelfKind::Value]),
-    (Convention::StartsWith("is_"), &[SelfKind::Ref, SelfKind::No]),
-    (Convention::Eq("to_mut"), &[SelfKind::RefMut]),
-    (Convention::StartsWith("to_"), &[SelfKind::Ref]),
+const CONVENTIONS: [(&[Convention], &[SelfKind]); 9] = [
+    (&[Convention::Eq("new")], &[SelfKind::No]),
+    (&[Convention::StartsWith("as_")], &[SelfKind::Ref, SelfKind::RefMut]),
+    (&[Convention::StartsWith("from_")], &[SelfKind::No]),
+    (&[Convention::StartsWith("into_")], &[SelfKind::Value]),
+    (&[Convention::StartsWith("is_")], &[SelfKind::Ref, SelfKind::No]),
+    (&[Convention::Eq("to_mut")], &[SelfKind::RefMut]),
+    (&[Convention::StartsWith("to_"), Convention::EndsWith("_mut")], &[SelfKind::RefMut]),
+
+    // Conversion using `to_` can use borrowed (non-Copy types) or owned (Copy types).
+    // Source: https://rust-lang.github.io/api-guidelines/naming.html#ad-hoc-conversions-follow-as_-to_-into_-conventions-c-conv
+    (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(false), Convention::ImplementsTrait(false)], &[SelfKind::Ref]),
+    (&[Convention::StartsWith("to_"), Convention::NotEndsWith("_mut"), Convention::IsSelfTypeCopy(true), Convention::ImplementsTrait(false)], &[SelfKind::Value]),
 ];
+
 enum Convention {
     Eq(&'static str),
     StartsWith(&'static str),
+    EndsWith(&'static str),
+    NotEndsWith(&'static str),
+    IsSelfTypeCopy(bool),
+    ImplementsTrait(bool),
 }
 
 impl Convention {
     #[must_use]
-    fn check(&self, other: &str) -> bool {
+    fn check<'tcx>(&self, cx: &LateContext<'tcx>, self_ty: &'tcx TyS<'tcx>, other: &str, is_trait_def: bool) -> bool {
         match *self {
             Self::Eq(this) => this == other,
             Self::StartsWith(this) => other.starts_with(this) && this != other,
+            Self::EndsWith(this) => other.ends_with(this) && this != other,
+            Self::NotEndsWith(this) => !Self::EndsWith(this).check(cx, self_ty, other, is_trait_def),
+            Self::IsSelfTypeCopy(is_true) => is_true == is_copy(cx, self_ty),
+            Self::ImplementsTrait(is_true) => is_true == is_trait_def,
         }
     }
 }
@@ -36,8 +51,17 @@ fn check(&self, other: &str) -> bool {
 impl fmt::Display for Convention {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
         match *self {
-            Self::Eq(this) => this.fmt(f),
-            Self::StartsWith(this) => this.fmt(f).and_then(|_| '*'.fmt(f)),
+            Self::Eq(this) => format!("`{}`", this).fmt(f),
+            Self::StartsWith(this) => format!("`{}*`", this).fmt(f),
+            Self::EndsWith(this) => format!("`*{}`", this).fmt(f),
+            Self::NotEndsWith(this) => format!("`~{}`", this).fmt(f),
+            Self::IsSelfTypeCopy(is_true) => {
+                format!("`self` type is{} `Copy`", if is_true { "" } else { " not" }).fmt(f)
+            },
+            Self::ImplementsTrait(is_true) => {
+                let (negation, s_suffix) = if is_true { ("", "s") } else { (" does not", "") };
+                format!("Method{} implement{} a trait", negation, s_suffix).fmt(f)
+            },
         }
     }
 }
@@ -49,27 +73,62 @@ pub(super) fn check<'tcx>(
     self_ty: &'tcx TyS<'tcx>,
     first_arg_ty: &'tcx TyS<'tcx>,
     first_arg_span: Span,
+    is_trait_item: bool,
 ) {
     let lint = if is_pub {
         WRONG_PUB_SELF_CONVENTION
     } else {
         WRONG_SELF_CONVENTION
     };
-    if let Some((ref conv, self_kinds)) = &CONVENTIONS.iter().find(|(ref conv, _)| conv.check(item_name)) {
+    if let Some((conventions, self_kinds)) = &CONVENTIONS.iter().find(|(convs, _)| {
+        convs
+            .iter()
+            .all(|conv| conv.check(cx, self_ty, item_name, is_trait_item))
+    }) {
         if !self_kinds.iter().any(|k| k.matches(cx, self_ty, first_arg_ty)) {
-            span_lint(
+            let suggestion = {
+                if conventions.len() > 1 {
+                    // Don't mention `NotEndsWith` when there is also `StartsWith` convention present
+                    let cut_ends_with_conv = conventions.iter().any(|conv| matches!(conv, Convention::StartsWith(_)))
+                        && conventions
+                            .iter()
+                            .any(|conv| matches!(conv, Convention::NotEndsWith(_)));
+
+                    let s = conventions
+                        .iter()
+                        .filter_map(|conv| {
+                            if (cut_ends_with_conv && matches!(conv, Convention::NotEndsWith(_)))
+                                || matches!(conv, Convention::ImplementsTrait(_))
+                            {
+                                None
+                            } else {
+                                Some(conv.to_string())
+                            }
+                        })
+                        .collect::<Vec<_>>()
+                        .join(" and ");
+
+                    format!("methods with the following characteristics: ({})", &s)
+                } else {
+                    format!("methods called {}", &conventions[0])
+                }
+            };
+
+            span_lint_and_help(
                 cx,
                 lint,
                 first_arg_span,
                 &format!(
-                    "methods called `{}` usually take {}; consider choosing a less ambiguous name",
-                    conv,
+                    "{} usually take {}",
+                    suggestion,
                     &self_kinds
                         .iter()
                         .map(|k| k.description())
                         .collect::<Vec<_>>()
                         .join(" or ")
                 ),
+                None,
+                "consider choosing a less ambiguous name",
             );
         }
     }
index f1335726736ca4d85f3e927091d078edd0a1247d..9f6a7c4db1736bb8ccf64749a23f1a8314741365 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
index 8d0c3b8e0fe89f39921c198656a553095e85e689..776f4c7b7413d90c62b6ddfb3937e914b51358cf 100644 (file)
@@ -1,5 +1,6 @@
 use crate::consts::{constant_simple, Constant};
-use crate::utils::{match_def_path, match_trait_method, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{match_def_path, match_trait_method, paths};
 use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
index acdc245456b5a1a508ce480e972b861bc44bf985..026ea50936a4a1520a23a7f4e3ceccd87d66ddfb 100644 (file)
@@ -1,3 +1,6 @@
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
+use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::ty::implements_trait;
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_span::symbol::sym;
 
 use crate::consts::{constant, Constant};
-use crate::utils::sugg::Sugg;
-use crate::utils::{
-    get_item_name, get_parent_expr, higher, implements_trait, in_constant, is_diagnostic_assoc_item, is_integer_const,
-    iter_input_pats, last_path_segment, match_qpath, snippet, snippet_opt, span_lint, span_lint_and_sugg,
-    span_lint_and_then, span_lint_hir_and_then, unsext, SpanlessEq,
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{
+    get_item_name, get_parent_expr, higher, in_constant, is_diagnostic_assoc_item, is_integer_const, iter_input_pats,
+    last_path_segment, match_qpath, unsext, SpanlessEq,
 };
 
 declare_clippy_lint! {
index 84a0df92f5b43ae45620a9ef0ca3de550ccb2d5a..3c6a7071c24a7e62cab0d4b2b49c7564e6ee57a3 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{snippet_opt, span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet_opt;
 use rustc_ast::ast::{
     BindingMode, Expr, ExprKind, GenericParamKind, Generics, Lit, LitFloatType, LitIntType, LitKind, Mutability,
     NodeId, Pat, PatKind, UnOp,
index b0998a80128cef12d73d13f2136b7c64e5672fd0..23554669d9774f030744697859e29a820fd005de 100644 (file)
@@ -1,7 +1,7 @@
-use crate::utils::qualify_min_const_fn::is_min_const_fn;
-use crate::utils::{
-    fn_has_unsatisfiable_preds, has_drop, is_entrypoint_fn, meets_msrv, span_lint, trait_ref_of_method,
-};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::qualify_min_const_fn::is_min_const_fn;
+use clippy_utils::ty::has_drop;
+use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, meets_msrv, trait_ref_of_method};
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, Constness, FnDecl, GenericParamKind, HirId};
index 985a66b6cfca24f333a57f0a2449e5bc356f0017..ff87828c2e777811450bca1be835d91e46e435ef 100644 (file)
@@ -5,7 +5,7 @@
 // [`missing_doc`]: https://github.com/rust-lang/rust/blob/cf9cf7c923eb01146971429044f216a3ca905e06/compiler/rustc_lint/src/builtin.rs#L415
 //
 
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
 use if_chain::if_chain;
 use rustc_ast::ast::{self, MetaItem, MetaItemKind};
 use rustc_ast::attr;
index da59c820999d9e4786e190bfc1de3cde1c9b202e..dd4488f3f026128e75c66ea22e215579bb0b75d4 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
 use rustc_ast::ast;
 use rustc_hir as hir;
 use rustc_lint::{self, LateContext, LateLintPass, LintContext};
index da3ae1d652f6c515862ebb2c74016b4c7e69c4f0..6a52de4f713644f965009ae3fd31bf77ad7232ee 100644 (file)
@@ -1,5 +1,6 @@
 use crate::consts::{constant, Constant};
-use crate::utils::{sext, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sext;
 use if_chain::if_chain;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
index c1773cef7a8b7f4c63b9af27269078a4ab91cf49..584daa5e11992a17921f4252592051ff8e598524 100644 (file)
@@ -1,6 +1,7 @@
 //! lint on multiple versions of a crate being used
 
-use crate::utils::{run_lints, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::run_lints;
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_hir::{Crate, CRATE_HIR_ID};
 use rustc_lint::{LateContext, LateLintPass};
index 908b7bb7ce00d13359c619bda5f54d812e89fb95..41bd07bcf1ea2fd2523a790792d2183762b67da1 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{match_def_path, paths, span_lint, trait_ref_of_method};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{match_def_path, paths, trait_ref_of_method};
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::TypeFoldable;
index d7239b328bbcd0a7f245ba19b827ffbf67f1b856..ef33e41a5fa9e6ca3c46413b1eabb5a81574537a 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{higher, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::higher;
 use rustc_hir as hir;
 use rustc_hir::intravisit;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
index df1cecb328cb151698aff17b375176e714e3619c..b9ba74c7d025284db2d7091c1f204b87e22e757f 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{is_type_diagnostic_item, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Mutability};
index 3f0b765df1561dc9543984506ea475dca498299a..0c09ddb80733d3ee7a16ce53e0afaee323535c25 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::subst::Subst;
index 9caacb5db7c9c67f00bf13a69a0f2ab66e1c38e6..7dfe12cd4ebc07a8ffce24664536c3cc87ad4807 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{higher, is_direct_expn_of, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{higher, is_direct_expn_of};
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 use rustc_hir::{BorrowKind, Expr, ExprKind, MatchSource, Mutability};
 use rustc_lint::{LateContext, LateLintPass};
index 40b236493a3135722fe1ded68054f20ca462a7d9..354e2c3fb7480548465e2c1597b0f2b03620a4f9 100644 (file)
@@ -2,7 +2,8 @@
 //!
 //! This lint is **warn** by default
 
-use crate::utils::{is_type_diagnostic_item, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_hir::Expr;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, Ty};
index 7687962bdd9bf1bfffe8cd772fd164421ed7a204..3e2b2782ed5ffc2c04e2dd21846fe49759ebc7ac 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{in_macro, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
 use if_chain::if_chain;
 use rustc_ast::ast::{BindingMode, Lifetime, Mutability, Param, PatKind, Path, TyKind};
 use rustc_errors::Applicability;
index f283ff1715fb667df833c98cebc41039603d04ee..db7b3423ad9df3d20f7437c8cf299112b83fa369 100644 (file)
@@ -2,8 +2,10 @@
 //!
 //! This lint is **warn** by default
 
-use crate::utils::sugg::Sugg;
-use crate::utils::{is_expn_of, parent_node_is_if_expr, snippet_with_applicability, span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{is_expn_of, parent_node_is_if_expr};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
index 1aadcfd87b60f73a1dc44626adc30300cae35d93..79d84da2dfc09c8af7686a9c592872729e7413e1 100644 (file)
@@ -2,7 +2,9 @@
 //!
 //! This lint is **warn** by default
 
-use crate::utils::{is_automatically_derived, snippet_opt, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_automatically_derived;
+use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BindingAnnotation, BorrowKind, Expr, ExprKind, Item, Mutability, Pat, PatKind};
index f449f397e7d6163e4f81ce25475ad21c3ac59e69..7fbffe04a3f5a1b38d9514307b8aed26dccc6c5a 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{snippet_with_applicability, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_with_applicability;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BindingAnnotation, Mutability, Node, Pat, PatKind};
index 30fe2d6225c8ae5004b029a41da5efc4d3ee50b4..91c97ef7c2a404dbfd319132454950180416bf11 100644 (file)
 //! ```
 //!
 //! This lint is **warn** by default.
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::{indent_of, snippet, snippet_block};
 use rustc_ast::ast;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::{original_sp, DUMMY_SP};
 use rustc_span::Span;
 
-use crate::utils::{indent_of, snippet, snippet_block, span_lint_and_help};
-
 declare_clippy_lint! {
     /// **What it does:** The lint checks for `if`-statements appearing in loops
     /// that contain a `continue` statement in either their main blocks or their
index d439577f9c33b9918dbf0e1d6fda38924c842a85..e70c248e87bea066b31200ca2829f764f5b10548 100644 (file)
@@ -1,8 +1,8 @@
-use crate::utils::ptr::get_spans;
-use crate::utils::{
-    get_trait_def_id, implements_trait, is_copy, is_self, is_type_diagnostic_item, multispan_sugg, paths, snippet,
-    snippet_opt, span_lint_and_then,
-};
+use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::ptr::get_spans;
+use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::ty::{implements_trait, is_copy, is_type_diagnostic_item};
+use clippy_utils::{get_trait_def_id, is_self, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::Attribute;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
@@ -335,5 +335,5 @@ fn borrow(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId, _: ty::BorrowKind)
 
     fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: HirId) {}
 
-    fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) { }
+    fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
 }
index a3293f1b3614925d017d33ce0e4a8546c0e92ef4..9852633b7342e48b8448d0c8228b2f9a3189581b 100644 (file)
@@ -1,3 +1,8 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{differing_macro_contexts, is_ok_ctor, is_some_ctor, meets_msrv};
+use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Body, Expr, ExprKind, LangItem, MatchSource, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -5,9 +10,6 @@
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::sym;
 
-use crate::utils;
-use if_chain::if_chain;
-
 declare_clippy_lint! {
     /// **What it does:**
     /// Suggests alternatives for useless applications of `?` in terminating expressions
@@ -138,13 +140,13 @@ fn emit_lint(cx: &LateContext<'_>, expr: &SomeOkCall<'_>) {
         SomeOkCall::OkCall(outer, inner) | SomeOkCall::SomeCall(outer, inner) => (outer, inner),
     };
 
-    utils::span_lint_and_sugg(
+    span_lint_and_sugg(
         cx,
         NEEDLESS_QUESTION_MARK,
         entire_expr.span,
         "question mark operator is useless here",
         "try",
-        format!("{}", utils::snippet(cx, inner_expr.span, r#""...""#)),
+        format!("{}", snippet(cx, inner_expr.span, r#""...""#)),
         Applicability::MachineApplicable,
     );
 }
@@ -158,7 +160,7 @@ fn is_some_or_ok_call<'a>(
         // Check outer expression matches CALL_IDENT(ARGUMENT) format
         if let ExprKind::Call(path, args) = &expr.kind;
         if let ExprKind::Path(QPath::Resolved(None, path)) = &path.kind;
-        if utils::is_some_ctor(cx, path.res) || utils::is_ok_ctor(cx, path.res);
+        if is_some_ctor(cx, path.res) || is_ok_ctor(cx, path.res);
 
         // Extract inner expression from ARGUMENT
         if let ExprKind::Match(inner_expr_with_q, _, MatchSource::TryDesugar) = &args[0].kind;
@@ -171,29 +173,34 @@ fn is_some_or_ok_call<'a>(
             // question mark operator
             let inner_expr = &args[0];
 
+            // if the inner expr is inside macro but the outer one is not, do not lint (#6921)
+            if  differing_macro_contexts(expr.span, inner_expr.span) {
+                return None;
+            }
+
             let inner_ty = cx.typeck_results().expr_ty(inner_expr);
             let outer_ty = cx.typeck_results().expr_ty(expr);
 
             // Check if outer and inner type are Option
-            let outer_is_some = utils::is_type_diagnostic_item(cx, outer_ty, sym::option_type);
-            let inner_is_some = utils::is_type_diagnostic_item(cx, inner_ty, sym::option_type);
+            let outer_is_some = is_type_diagnostic_item(cx, outer_ty, sym::option_type);
+            let inner_is_some = is_type_diagnostic_item(cx, inner_ty, sym::option_type);
 
             // Check for Option MSRV
-            let meets_option_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV);
+            let meets_option_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_OPTION_MSRV);
             if outer_is_some && inner_is_some && meets_option_msrv {
                 return Some(SomeOkCall::SomeCall(expr, inner_expr));
             }
 
             // Check if outer and inner type are Result
-            let outer_is_result = utils::is_type_diagnostic_item(cx, outer_ty, sym::result_type);
-            let inner_is_result = utils::is_type_diagnostic_item(cx, inner_ty, sym::result_type);
+            let outer_is_result = is_type_diagnostic_item(cx, outer_ty, sym::result_type);
+            let inner_is_result = is_type_diagnostic_item(cx, inner_ty, sym::result_type);
 
             // Additional check: if the error type of the Result can be converted
             // via the From trait, then don't match
             let does_not_call_from = !has_implicit_error_from(cx, expr, inner_expr);
 
             // Must meet Result MSRV
-            let meets_result_msrv = utils::meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV);
+            let meets_result_msrv = meets_msrv(nqml.msrv.as_ref(), &NEEDLESS_QUESTION_MARK_RESULT_MSRV);
             if outer_is_result && inner_is_result && does_not_call_from && meets_result_msrv {
                 return Some(SomeOkCall::OkCall(expr, inner_expr));
             }
index 41cf541ecf5ef59d8e814bae9ffde7d72d56f511..e93de8a252a34befc63b1aac34164f0bf266213b 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
index ec0ad58ca9c3e9558a726b2ce9bad59b00b5e46f..4b935c7b906aec25e49327152d053963c7398935 100644 (file)
@@ -1,11 +1,12 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{self, get_trait_def_id, paths};
 use if_chain::if_chain;
 use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use crate::utils::{self, paths, span_lint};
-
 declare_clippy_lint! {
     /// **What it does:**
     /// Checks for the usage of negated comparison operators on types which only implement
@@ -59,8 +60,8 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 let ty = cx.typeck_results().expr_ty(left);
 
                 let implements_ord = {
-                    if let Some(id) = utils::get_trait_def_id(cx, &paths::ORD) {
-                        utils::implements_trait(cx, ty, id, &[])
+                    if let Some(id) = get_trait_def_id(cx, &paths::ORD) {
+                        implements_trait(cx, ty, id, &[])
                     } else {
                         return;
                     }
@@ -68,7 +69,7 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 
                 let implements_partial_ord = {
                     if let Some(id) = cx.tcx.lang_items().partial_ord_trait() {
-                        utils::implements_trait(cx, ty, id, &[])
+                        implements_trait(cx, ty, id, &[])
                     } else {
                         return;
                     }
index ef7cc65cfcf0a36fe3416a70fd35e5aa07bd9b7d..7b00879251f768c4d41b2cab35b4af212812587d 100644 (file)
@@ -1,3 +1,4 @@
+use clippy_utils::diagnostics::span_lint;
 use if_chain::if_chain;
 use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
@@ -5,7 +6,6 @@
 use rustc_span::source_map::Span;
 
 use crate::consts::{self, Constant};
-use crate::utils::span_lint;
 
 declare_clippy_lint! {
     /// **What it does:** Checks for multiplication by -1 as a form of negation.
index de2899c3462a4f6f8b71a0a727140c09f93e6232..3789572ad439ea1929776238ac201d56a52634d7 100644 (file)
@@ -1,6 +1,8 @@
-use crate::utils::paths;
-use crate::utils::sugg::DiagnosticBuilderExt;
-use crate::utils::{get_trait_def_id, return_ty, span_lint_hir_and_then};
+use clippy_utils::diagnostics::span_lint_hir_and_then;
+use clippy_utils::paths;
+use clippy_utils::source::snippet;
+use clippy_utils::sugg::DiagnosticBuilderExt;
+use clippy_utils::{get_trait_def_id, return_ty};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -61,7 +63,10 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
     #[allow(clippy::too_many_lines)]
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
         if let hir::ItemKind::Impl(hir::Impl {
-            of_trait: None, items, ..
+            of_trait: None,
+            ref generics,
+            items,
+            ..
         }) = item.kind
         {
             for assoc_item in items {
@@ -125,6 +130,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
                                         }
                                     }
 
+                                    let generics_sugg = snippet(cx, generics.span, "");
                                     span_lint_hir_and_then(
                                         cx,
                                         NEW_WITHOUT_DEFAULT,
@@ -139,7 +145,7 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
                                                 cx,
                                                 item.span,
                                                 "try this",
-                                                &create_new_without_default_suggest_msg(self_ty),
+                                                &create_new_without_default_suggest_msg(self_ty, &generics_sugg),
                                                 Applicability::MaybeIncorrect,
                                             );
                                         },
@@ -154,12 +160,12 @@ fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
     }
 }
 
-fn create_new_without_default_suggest_msg(ty: Ty<'_>) -> String {
+fn create_new_without_default_suggest_msg(ty: Ty<'_>, generics_sugg: &str) -> String {
     #[rustfmt::skip]
     format!(
-"impl Default for {} {{
+"impl{} Default for {} {{
     fn default() -> Self {{
         Self::new()
     }}
-}}", ty)
+}}", generics_sugg, ty)
 }
index 69302d695ce0a31304aadca1c8c6fa2dcdd19c1b..83953a16bc8d1e80120104a98451e58a17817916 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{has_drop, snippet_opt, span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::has_drop;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{BinOpKind, BlockCheckMode, Expr, ExprKind, Stmt, StmtKind, UnsafeSource};
index 3e1db233696fe50042b6c80d040cb3059829a190..aa1d8fbe300ce9f83d8c6c543791fb613e5699b0 100644 (file)
@@ -4,6 +4,9 @@
 
 use std::ptr;
 
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::in_constant;
+use if_chain::if_chain;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{
@@ -18,9 +21,6 @@
 use rustc_span::{InnerSpan, Span, DUMMY_SP};
 use rustc_typeck::hir_ty_to_ty;
 
-use crate::utils::{in_constant, span_lint_and_then};
-use if_chain::if_chain;
-
 // FIXME: this is a correctness problem but there's no suitable
 // warn-by-default category.
 declare_clippy_lint! {
@@ -179,17 +179,15 @@ fn is_value_unfrozen_poly<'tcx>(cx: &LateContext<'tcx>, body_id: BodyId, ty: Ty<
 fn is_value_unfrozen_expr<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
     let substs = cx.typeck_results().node_substs(hir_id);
 
-    let result = cx
-        .tcx
-        .const_eval_resolve(
-            cx.param_env,
-            ty::Unevaluated {
-                def: ty::WithOptConstParam::unknown(def_id),
-                substs,
-                promoted: None
-            },
-            None
-        );
+    let result = cx.tcx.const_eval_resolve(
+        cx.param_env,
+        ty::Unevaluated {
+            def: ty::WithOptConstParam::unknown(def_id),
+            substs,
+            promoted: None,
+        },
+        None,
+    );
     is_value_unfrozen_raw(cx, result, ty)
 }
 
index d5222a030d7afbb495da5f060b9b44d7313f6255..4c8bceaf2cb8bf90e66be13c3fbb72941d56dae5 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::{span_lint, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use rustc_ast::ast::{
     Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat,
     PatKind,
index 07ca196990da9b71dcafa0ec395629b01f4b562b..c61dff4b8e04360cb9a2a1fcfb7d4b595ad36f16 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{match_type, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::paths;
+use clippy_utils::ty::match_type;
 use rustc_ast::ast::LitKind;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
index fd653044a1bcb9fb6442041c6e2b4e1f39d90c21..a0bc324e02692c3d9363966beb115513ccfcb84a 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{is_direct_expn_of, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_direct_expn_of;
 use if_chain::if_chain;
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_lint::{EarlyContext, EarlyLintPass};
index 9ef0d267b0b20b49b691ae5bfdf49c637fb357c8..a76a4a33f1f3b12a491ac1c82ae5b2f3f4f0608e 100644 (file)
@@ -1,9 +1,10 @@
-use crate::utils;
-use crate::utils::eager_or_lazy;
-use crate::utils::sugg::Sugg;
-use crate::utils::{is_type_diagnostic_item, paths, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::paths;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::usage::contains_return_break_continue_macro;
+use clippy_utils::{eager_or_lazy, get_enclosing_block, in_macro, match_qpath};
 use if_chain::if_chain;
-
 use rustc_errors::Applicability;
 use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
@@ -108,7 +109,7 @@ fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> {
 /// If this is the else body of an if/else expression, then we need to wrap
 /// it in curly braces. Otherwise, we don't.
 fn should_wrap_in_braces(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    utils::get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| {
+    get_enclosing_block(cx, expr.hir_id).map_or(false, |parent| {
         let mut should_wrap = false;
 
         if let Some(Expr {
@@ -158,15 +159,15 @@ fn detect_option_if_let_else<'tcx>(
     expr: &'_ Expr<'tcx>,
 ) -> Option<OptionIfLetElseOccurence> {
     if_chain! {
-        if !utils::in_macro(expr.span); // Don't lint macros, because it behaves weirdly
+        if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly
         if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind;
         if arms.len() == 2;
         if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already
         if let PatKind::TupleStruct(struct_qpath, &[inner_pat], _) = &arms[0].pat.kind;
-        if utils::match_qpath(struct_qpath, &paths::OPTION_SOME);
+        if match_qpath(struct_qpath, &paths::OPTION_SOME);
         if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind;
-        if !utils::usage::contains_return_break_continue_macro(arms[0].body);
-        if !utils::usage::contains_return_break_continue_macro(arms[1].body);
+        if !contains_return_break_continue_macro(arms[0].body);
+        if !contains_return_break_continue_macro(arms[1].body);
         then {
             let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
             let some_body = extract_body_from_arm(&arms[0])?;
index 3c041bac234a589f8034f1dd5ede89db0b6c1c8b..cf667c6e805341f46c18c61e5e4773c8bd562b5f 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{span_lint, SpanlessEq};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::SpanlessEq;
 use if_chain::if_chain;
 use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
index 207423a18614927f30960ba847b944170fbca91e..d32b937b209c5324f4fbf1e4b518062387b04a75 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{find_macro_calls, is_type_diagnostic_item, return_ty, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{find_macro_calls, return_ty};
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
 use rustc_lint::{LateContext, LateLintPass};
index 359620cc079752b806be469b03505d40aa4a2e1f..d06e7f8fe1e0e58eef7f89cf37700c67da0d67bc 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{is_expn_of, match_panic_call, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{is_expn_of, match_panic_call};
 use if_chain::if_chain;
 use rustc_hir::Expr;
 use rustc_lint::{LateContext, LateLintPass};
index aca1ed5ca6563144b4e3e9fc3a925f816a05ffd9..1251ddd9a02733bd55e95c68c03496ed22d3f03b 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{is_automatically_derived, span_lint_hir};
+use clippy_utils::diagnostics::span_lint_hir;
+use clippy_utils::is_automatically_derived;
 use if_chain::if_chain;
 use rustc_hir::{Impl, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
index ff700aa51460789cc66d0aa7779ce65a24ec0cb2..9a5b1c3b9442a7e7f8658df06e98f5233dccd107 100644 (file)
@@ -1,6 +1,9 @@
 use std::cmp;
 
-use crate::utils::{is_copy, is_self_ty, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_self_ty;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_copy;
 use if_chain::if_chain;
 use rustc_ast::attr;
 use rustc_errors::Applicability;
index 4a7b0ad07aaebbcef27653e56e81f6d873906a10..95ffae28d8c27847912b8b8dd3a07d0e2a451d5c 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{is_type_diagnostic_item, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
index e76c8624b6fe8ac50cd27fbe9b59643fefbdf458..4550b367da4bf92fd9a39493554c9e105dc2621b 100644 (file)
@@ -1,6 +1,7 @@
-use crate::utils::{last_path_segment, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::last_path_segment;
 use rustc_hir::{
-    intravisit, Body, Expr, ExprKind, PatField, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatKind,
+    intravisit, Body, Expr, ExprKind, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatField, PatKind,
     QPath, Stmt, StmtKind,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
index c9d18c3cb7287551e439bb15d3b57d05d29806e4..9cf00c953b95f16afd34ec6e047f944d534c840e 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
 use if_chain::if_chain;
 use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind, UnOp};
 use rustc_errors::Applicability;
index 6ea2d8b06d81cd60319e7d5cb0d8a9d257f4f99f..be686b1b0cd814f6c7fffd341a2a97fad8dc377c 100644 (file)
@@ -1,10 +1,10 @@
 //! Checks for usage of  `&Vec[_]` and `&String`.
 
-use crate::utils::ptr::get_spans;
-use crate::utils::{
-    is_allowed, is_type_diagnostic_item, match_qpath, match_type, paths, snippet_opt, span_lint, span_lint_and_sugg,
-    span_lint_and_then, walk_ptrs_hir_ty,
-};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::ptr::get_spans;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::{is_type_diagnostic_item, match_type, walk_ptrs_hir_ty};
+use clippy_utils::{is_allowed, match_qpath, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{
index 3be792ce5e4fa5c3bed6da95be0393a11e3892fa..5796c59c8b3fb6a53a1c6c3eddc5b78f19bf1404 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
+use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
@@ -40,7 +42,7 @@
 
 impl LateLintPass<'_> for PtrEq {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if utils::in_macro(expr.span) {
+        if in_macro(expr.span) {
             return;
         }
 
@@ -54,10 +56,10 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
                 if_chain! {
                     if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left);
                     if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right);
-                    if let Some(left_snip) = utils::snippet_opt(cx, left_var.span);
-                    if let Some(right_snip) = utils::snippet_opt(cx, right_var.span);
+                    if let Some(left_snip) = snippet_opt(cx, left_var.span);
+                    if let Some(right_snip) = snippet_opt(cx, right_var.span);
                     then {
-                        utils::span_lint_and_sugg(
+                        span_lint_and_sugg(
                             cx,
                             PTR_EQ,
                             expr.span,
index e0996804a5934a8ad77c83a30fc363c3452c7e82..c04b42552569fec67f6529c82aa8e161846f6ed4 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{snippet_opt, span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::source::snippet_opt;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
index 6c480d48c7561245a0ab0150e49edcf1a5803f1b..2054255a7c9dee1a2884c8c84894fdbe057e0d3d 100644 (file)
@@ -1,3 +1,8 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{eq_expr_value, match_def_path, match_qpath, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
@@ -6,12 +11,6 @@
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::sym;
 
-use crate::utils::sugg::Sugg;
-use crate::utils::{
-    eq_expr_value, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet_with_applicability,
-    span_lint_and_sugg,
-};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for expressions that could be replaced by the question mark operator.
     ///
index 59503817c0fccfc97719e2d355defa325e8fb0fe..95b21489eb50e9f706c41ecd6de9ad432391bcae 100644 (file)
@@ -1,4 +1,9 @@
 use crate::consts::{constant, Constant};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path};
+use clippy_utils::{higher, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::RangeLimits;
 use rustc_errors::Applicability;
 use rustc_span::symbol::Ident;
 use std::cmp::Ordering;
 
-use crate::utils::sugg::Sugg;
-use crate::utils::{
-    get_parent_expr, in_constant, is_integer_const, meets_msrv, single_segment_path, snippet, snippet_opt,
-    snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then,
-};
-use crate::utils::{higher, SpanlessEq};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for zipping a collection with the range of
     /// `0.._.len()`.
index f90d48205633edebe953f6f3de291433cad8b508..3abe619207e9106317ee27b74e26848498e7752b 100644 (file)
@@ -1,7 +1,7 @@
-use crate::utils::{
-    fn_has_unsatisfiable_preds, has_drop, is_copy, is_type_diagnostic_item, match_def_path, paths, snippet_opt,
-    span_lint_hir, span_lint_hir_and_then, walk_ptrs_ty_depth,
-};
+use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, walk_ptrs_ty_depth};
+use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_data_structures::{fx::FxHashMap, transitive_relation::TransitiveRelation};
 use rustc_errors::Applicability;
@@ -556,7 +556,7 @@ fn visit_terminator(&mut self, terminator: &mir::Terminator<'_>, _loc: mir::Loca
                     mir::Operand::Copy(p) | mir::Operand::Move(p) => {
                         self.possible_borrower.add(p.local, *dest);
                     },
-                    _ => (),
+                    mir::Operand::Constant(..) => (),
                 }
             }
         }
@@ -578,7 +578,7 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
 
     let mut visit_op = |op: &mir::Operand<'_>| match op {
         mir::Operand::Copy(p) | mir::Operand::Move(p) => visit(p.local),
-        _ => (),
+        mir::Operand::Constant(..) => (),
     };
 
     match rvalue {
index f398b3fff25a328ec6b5d94c249a9e78de88fef5..2977a108d141bfa72abe922114e04f51e7a84c33 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
+use clippy_utils::source::snippet_with_applicability;
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_ast::visit as ast_visit;
index 3d585cd27a3d07c9a5e808526767eae4cdaacd8d..061526c6f09fa3b67692528d7a73d5b9d8f5b47c 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_ast::ast::{Block, Expr, ExprKind, Stmt, StmtKind};
 use rustc_ast::visit::{walk_expr, Visitor};
 use rustc_lint::{EarlyContext, EarlyLintPass};
index 9688ef393313ce7ad18623849ae8f5a4af34607a..abebd4227975ef3425c2edb2ffefc5ebc7613761 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{meets_msrv, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::meets_msrv;
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
index c876bae2303ad29f0252d0ec425d1b9ca17374c7..e091095de136a5f941746e62c4ca797d1bd48381 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::span_lint_and_then;
+use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind, VisibilityKind};
 use rustc_lint::{LateContext, LateLintPass};
index e5ced13514f793088205d3a3b0284355f3cc6852..6da7b5fbcc8a009a40856c9f5a71b609a37e23ae 100644 (file)
@@ -1,3 +1,6 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_lang_item;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, LangItem};
@@ -5,8 +8,6 @@
 use rustc_middle::{lint::in_external_macro, ty::TyS};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use crate::utils::{is_type_lang_item, snippet_with_applicability, span_lint_and_sugg};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for redundant slicing expressions which use the full range, and
     /// do not change the type.
index fcfa3c12755af37e0979aac06021a48ee8473d99..32b57698ec54dc6481c0daa7468ec3b919860d25 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{meets_msrv, snippet, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::meets_msrv;
+use clippy_utils::source::snippet;
 use rustc_ast::ast::{Item, ItemKind, Ty, TyKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
index 8cd6692ce03a09ceb9b42647865d101ec3c42771..0922cfa494e6fabfddfe754417012c7994a0123f 100644 (file)
@@ -1,12 +1,13 @@
-use crate::utils::{last_path_segment, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::last_path_segment;
+use clippy_utils::source::snippet;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
 use rustc_hir::{GenericArg, Mutability, Ty, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::symbol::sym;
 
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-
 declare_clippy_lint! {
     /// **What it does:** Checks for usage of `&Option<&T>`.
     ///
index e1450466a7c22454374a4dabce94853ecbeed154..d6336389b0af12308a586f8ccc28e81707196895 100644 (file)
@@ -1,5 +1,7 @@
-use crate::utils::sugg::Sugg;
-use crate::utils::{in_macro, snippet_opt, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
+use clippy_utils::source::{snippet_opt, snippet_with_applicability};
+use clippy_utils::sugg::Sugg;
 use if_chain::if_chain;
 use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp};
 use rustc_errors::Applicability;
index 1edea6131489323f493f2e20d60f360a39ee311a..1cc332de894e16eb2527176ea5bf1a2f85d01dce 100644 (file)
@@ -1,5 +1,6 @@
 use crate::consts::{constant, Constant};
-use crate::utils::{match_def_path, paths, span_lint, span_lint_and_help};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::{LitKind, StrStyle};
 use rustc_data_structures::fx::FxHashSet;
index d34e744eb944cd0631f0bc00b6d55ffdc6b6585c..63e5ec69e66d1ada4bb34bb3cb96e648f9670f34 100644 (file)
@@ -1,5 +1,8 @@
 use crate::consts::{constant_context, Constant};
-use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
index 40c0f1f45895ba0af5ed1341e12d21265bc6f637..8995ae431adadb1e357ee846ce721a7abd3e2681 100644 (file)
@@ -1,3 +1,6 @@
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{fn_def_id, in_macro, match_qpath};
 use if_chain::if_chain;
 use rustc_ast::ast::Attribute;
 use rustc_errors::Applicability;
@@ -11,8 +14,6 @@
 use rustc_span::source_map::Span;
 use rustc_span::sym;
 
-use crate::utils::{fn_def_id, in_macro, match_qpath, snippet_opt, span_lint_and_sugg, span_lint_and_then};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for `let`-bindings, which are subsequently
     /// returned.
index e096c9aebc122fe8b7a53ef15d446fce2512d442..e7925c4fbdeffdad43968210c2f93ce0b4097dd6 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{eq_expr_value, snippet, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::eq_expr_value;
+use clippy_utils::source::snippet;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
index 839c995e52562ab795b3c3e2dfc26b9ad62d06d9..f61af15fbed0ebbf5edaaa357a7c23bf0dd711e5 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{in_macro, snippet_with_macro_callsite, span_lint_and_sugg, sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_macro_callsite;
+use clippy_utils::{in_macro, sugg};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Block, ExprKind};
index 90cf1b6c8613577bbf31d523844a05494dbd5169..169f7d26285cd902c8abb1835e093c3542739c92 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{get_trait_def_id, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{get_trait_def_id, paths};
 use rustc_hir::{Impl, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
index 32f6bc74642ca8c17e12ecd805184eceb1a21670..612d2fd84cb6fd70029f83556a2c007edcb849bd 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{contains_name, higher, iter_input_pats, snippet, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::{contains_name, higher, iter_input_pats};
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
     Block, Body, Expr, ExprKind, FnDecl, Guard, HirId, Local, MutTy, Pat, PatKind, Path, QPath, StmtKind, Ty, TyKind,
index 1fc4ff5c2e61fcad025432b360ce6c422e8a933c..c9d72aabb6a3cb68d6d7dc14717e88fa103f6387 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{in_macro, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
 use if_chain::if_chain;
 use rustc_ast::{Item, ItemKind, UseTreeKind};
 use rustc_errors::Applicability;
index 87e386baadc54ec34190b868d9e2ec0a921d444b..09e00866815568a8478b4097b55b0efde6683e2d 100644 (file)
@@ -1,7 +1,8 @@
 //! Lint on use of `size_of` or `size_of_val` of T in an expression
 //! expecting a count of T
 
-use crate::utils::{match_def_path, paths, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
 use rustc_hir::BinOpKind;
 use rustc_hir::{Expr, ExprKind};
index 96f6881556cf380682e482615632a82aebc16243..d55a83f16136adc7d3de61e07a69ea9372d7a17d 100644 (file)
@@ -1,5 +1,6 @@
-use crate::utils::sugg::Sugg;
-use crate::utils::{get_enclosing_block, match_qpath, span_lint_and_then, SpanlessEq};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{get_enclosing_block, match_qpath, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
index 276a9338819d9303c544411021dd419f2d4daa19..65790375c737946cde63eabca848e69fc7587d70 100644 (file)
@@ -1,7 +1,6 @@
-use crate::utils::{is_slice_of_primitives, span_lint_and_then, sugg::Sugg};
-
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{is_slice_of_primitives, sugg::Sugg};
 use if_chain::if_chain;
-
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
index 31dd5965473d3e9c0affb5c1f2a627de8cda1ff8..99ca7ef77a5c5ed281cda4d2d2844a7912eba9df 100644 (file)
@@ -1,3 +1,9 @@
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::SpanlessEq;
+use clippy_utils::{get_parent_expr, is_allowed, match_function_call, method_calls, paths};
+use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, LangItem, QPath};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -7,14 +13,6 @@
 use rustc_span::source_map::Spanned;
 use rustc_span::sym;
 
-use if_chain::if_chain;
-
-use crate::utils::SpanlessEq;
-use crate::utils::{
-    get_parent_expr, is_allowed, is_type_diagnostic_item, match_function_call, method_calls, paths, span_lint,
-    span_lint_and_help, span_lint_and_sugg,
-};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for string appends of the form `x = x + y` (without
     /// `let`!).
@@ -205,7 +203,6 @@ fn is_add(cx: &LateContext<'_>, src: &Expr<'_>, target: &Expr<'_>) -> bool {
 
 impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        use crate::utils::{snippet, snippet_with_applicability};
         use rustc_ast::LitKind;
 
         if_chain! {
@@ -287,6 +284,35 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
                 }
             }
         }
+
+        if_chain! {
+            if let ExprKind::MethodCall(path, _, [recv], _) = &e.kind;
+            if path.ident.name == sym!(into_bytes);
+            if let ExprKind::MethodCall(path, _, [recv], _) = &recv.kind;
+            if matches!(&*path.ident.name.as_str(), "to_owned" | "to_string");
+            if let ExprKind::Lit(lit) = &recv.kind;
+            if let LitKind::Str(lit_content, _) = &lit.node;
+
+            if lit_content.as_str().is_ascii();
+            if lit_content.as_str().len() <= MAX_LENGTH_BYTE_STRING_LIT;
+            if !recv.span.from_expansion();
+            then {
+                let mut applicability = Applicability::MachineApplicable;
+
+                span_lint_and_sugg(
+                    cx,
+                    STRING_LIT_AS_BYTES,
+                    e.span,
+                    "calling `into_bytes()` on a string literal",
+                    "consider using a byte string literal instead",
+                    format!(
+                        "b{}.to_vec()",
+                        snippet_with_applicability(cx, recv.span, r#""..""#, &mut applicability)
+                    ),
+                    applicability,
+                );
+            }
+        }
     }
 }
 
index 9acc47deb066043ca8d53a06a8c829c7fd0bc7cc..46f423204a21d3282ed3cb1097e9826b32980bf1 100644 (file)
@@ -1,5 +1,6 @@
-use crate::utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter};
-use crate::utils::{snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::ast_utils::{eq_id, is_useless_with_eq_exprs, IdentIter};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
 use core::ops::{Add, AddAssign};
 use if_chain::if_chain;
 use rustc_ast::ast::{BinOpKind, Expr, ExprKind, StmtKind};
index 0b7d08cb1645a11f74609a22009d6f87dc482ec4..99e3d818b798b53ed3253fa1762f15c14f4ebdff 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{get_trait_def_id, span_lint, trait_ref_of_method};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{get_trait_def_id, paths, trait_ref_of_method};
 use if_chain::if_chain;
 use rustc_hir as hir;
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
@@ -158,7 +159,7 @@ fn check_binop(
     expected_ops: &[hir::BinOpKind],
 ) -> Option<&'static str> {
     let mut trait_ids = vec![];
-    let [krate, module] = crate::utils::paths::OPS_MODULE;
+    let [krate, module] = paths::OPS_MODULE;
 
     for &t in traits {
         let path = [krate, module, t];
index 9d8a0c248334f582af850002b48f112be4447374..14519eaa962e33dd47adcec19b5a5ff9dade6079 100644 (file)
@@ -1,7 +1,8 @@
-use crate::utils::sugg::Sugg;
-use crate::utils::{
-    differing_macro_contexts, eq_expr_value, is_type_diagnostic_item, snippet_with_applicability, span_lint_and_then,
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{differing_macro_contexts, eq_expr_value};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, StmtKind};
index 74ccd9235de85d847daf593d4cc3a0018db586b1..88bd2feaaddae6b893b8b67f5ee817a6f21a241f 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_sugg;
 use rustc_ast::ast;
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
index fb891866364ccbc9730697e739d923681d9b8094..8ef25dc816c65a995d413faaf79de5f142f9861e 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{is_adjusted, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_adjusted;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
index eeda39bfa20874fc2fd856767de67dd698a2fe39..c66a596c784611e7afcb5bd605524c83a1090df1 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{match_def_path, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::match_def_path;
+use clippy_utils::source::snippet_with_applicability;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
index 84ec2aa18abcc01336ddb8fd497b43a185a4fd59..42ec14c31b5b1aec4bf6586f2b256842b6454796 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{is_diagnostic_assoc_item, match_def_path, path_to_local_id, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{is_diagnostic_assoc_item, match_def_path, path_to_local_id, paths};
 use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
index daff5f81e8c34c926a830089025eff5294a7d540..3ff27c3bcf49f0447f61762e1675620f3d6e8321 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_help, SpanlessHash};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::{in_macro, SpanlessHash};
 use if_chain::if_chain;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
index ce87defaa940622b1978111b9a8f35d7197e6288..25d0543c8611c831d897496ee3d59588a2d0c5be 100644 (file)
@@ -1,5 +1,5 @@
 use super::CROSSPOINTER_TRANSMUTE;
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
index c1870f5208b4502071501f30f50d604c0bf46b92..47d58bd30db51b9934d71abb261c1a5d38957ed9 100644 (file)
@@ -12,7 +12,7 @@
 mod utils;
 mod wrong_transmute;
 
-use crate::utils::{in_constant, match_def_path, paths};
+use clippy_utils::{in_constant, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
index 562d880e39afbffefa4cc996a9bdf0793fc2c61a..72489f27cd32856d34695c980cfcb445971665f9 100644 (file)
@@ -1,5 +1,6 @@
 use super::TRANSMUTE_FLOAT_TO_INT;
-use crate::utils::{span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
 use if_chain::if_chain;
 use rustc_ast as ast;
 use rustc_errors::Applicability;
index 5b609f906a3d7d9070ac98e7c55166318b222947..cc0a5643e2a7d35295de3f9eff785371fdbc1ad7 100644 (file)
@@ -1,5 +1,6 @@
 use super::TRANSMUTE_INT_TO_BOOL;
-use crate::utils::{span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
 use rustc_ast as ast;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
index 29d2450618a7e7ef5b3c7554b2cd35e7aa513bc5..8f884e6a4a17b74203247569d2f9b3a925a60c7c 100644 (file)
@@ -1,5 +1,6 @@
 use super::TRANSMUTE_INT_TO_CHAR;
-use crate::utils::{span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
 use rustc_ast as ast;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
index f83fba8966a11ac1399300c57c5207e4ac48a64f..2b6a4cff81eb5d60549b0dc4d97497e37f89e783 100644 (file)
@@ -1,5 +1,6 @@
 use super::TRANSMUTE_INT_TO_FLOAT;
-use crate::utils::{span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
index f4e60a3020cf58367976647d181e7333012bbfdb..7b646bfc0c6d13e0bb544d4aef8fede6beb8f0cb 100644 (file)
@@ -1,5 +1,6 @@
 use super::TRANSMUTE_PTR_TO_PTR;
-use crate::utils::{span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
index f5dbbbe33bc640e5e1ca474875d1a92cacc1498d..f14eef936453114abb3b4814fafe2cfe09c95826 100644 (file)
@@ -1,6 +1,7 @@
 use super::utils::get_type_snippet;
 use super::TRANSMUTE_PTR_TO_REF;
-use crate::utils::{span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Mutability, QPath};
 use rustc_lint::LateContext;
index 01b00bb0a222998c1df3ce3b6f29c62ce04ffcab..d105e37abf9c08281553f4298958db2e2f1bc9c6 100644 (file)
@@ -1,5 +1,7 @@
 use super::{TRANSMUTE_BYTES_TO_STR, TRANSMUTE_PTR_TO_PTR};
-use crate::utils::{snippet, span_lint_and_sugg, span_lint_and_then, sugg};
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet;
+use clippy_utils::sugg;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Mutability};
index dea896622f11c5727efb8e9d7f83959832a9adb0..e2c6d130f3c9cd40c537732af1e4b02e8305e0a7 100644 (file)
@@ -1,6 +1,7 @@
 use super::utils::can_be_expressed_as_pointer_cast;
 use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS;
-use crate::utils::{span_lint_and_then, sugg};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
index 503c5e0ff3822c19a0a723716eb290b18f20c203..de9277e016e3a7cbbf447af459d3547835d83a86 100644 (file)
@@ -1,6 +1,7 @@
 use super::utils::is_layout_incompatible;
 use super::UNSOUND_COLLECTION_TRANSMUTE;
-use crate::utils::{match_def_path, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{match_def_path, paths};
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
index 83441514af0511fa529ec9fdca8a83de00ec6588..445bcf60fa71a68e4481a945f4abda3a012d2cb3 100644 (file)
@@ -1,5 +1,6 @@
 use super::USELESS_TRANSMUTE;
-use crate::utils::{span_lint, span_lint_and_then, sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
+use clippy_utils::sugg;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
index 55008d8ec3f16e8ab587766037c419bd3e103d2a..c6d0d63b0b5426f32f9c1cc3a3eeca1a50ca8885 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{is_normalizable, last_path_segment, snippet};
+use clippy_utils::last_path_segment;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_normalizable;
 use if_chain::if_chain;
 use rustc_hir::{Expr, GenericArg, QPath, TyKind};
 use rustc_lint::LateContext;
index d6d77f2c834569ebec9b106b23047b8b4e5e0249..2118f3d6950041ee585ad613ed6ef139872d5a32 100644 (file)
@@ -1,5 +1,5 @@
 use super::WRONG_TRANSMUTE;
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
index 2ba2b646f004fba256c2d05cf061ce2f2bdd6e80..d42cdde110e7789e95cc5eb48f63af9d2093c524 100644 (file)
@@ -1,5 +1,6 @@
 use crate::consts::{constant_context, Constant};
-use crate::utils::{match_qpath, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{match_qpath, paths};
 use if_chain::if_chain;
 use rustc_ast::LitKind;
 use rustc_hir::{Expr, ExprKind};
index 73e3a04aec98798f93600e88ae791e3d7e6cc47c..e61058c2749083b616ae5676375bef2e2f9aff25 100644 (file)
@@ -1,7 +1,7 @@
-use crate::utils::{
-    differing_macro_contexts, in_macro, is_type_diagnostic_item, match_def_path, match_qpath, paths, snippet,
-    snippet_with_macro_callsite, span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::{snippet, snippet_with_macro_callsite};
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{differing_macro_contexts, in_macro, match_def_path, match_qpath, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, LangItem, MatchSource, QPath};
index 81090040d92e0af42b1f8a9a700305119d6bc3d8..d68c6db4e23343b4055b4a0f55ee1190aa400616 100644 (file)
@@ -1,3 +1,7 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::{match_path, paths};
+use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{
     self as hir, GenericArg, GenericBounds, GenericParamKind, HirId, Lifetime, MutTy, Mutability, Node, QPath,
@@ -5,10 +9,6 @@
 };
 use rustc_lint::LateContext;
 
-use if_chain::if_chain;
-
-use crate::utils::{match_path, paths, snippet, span_lint_and_sugg};
-
 use super::BORROWED_BOX;
 
 pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, mut_ty: &MutTy<'_>) -> bool {
index 6aa98e435e1605df715f9389304be86a09d947a5..d8b1953457ccc961f4803370e7195540ee168ca3 100644 (file)
@@ -1,9 +1,9 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_ty_param_diagnostic_item;
 use rustc_hir::{self as hir, def_id::DefId, QPath};
 use rustc_lint::LateContext;
 use rustc_span::symbol::sym;
 
-use crate::utils::{is_ty_param_diagnostic_item, span_lint_and_help};
-
 use super::BOX_VEC;
 
 pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
index 47eb4ede4e422cb32017a6269fbefa0eac0c5695..a9fbe7aa315c823dcdff245207507210cb501964 100644 (file)
@@ -1,8 +1,8 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::{match_def_path, paths};
 use rustc_hir::{self as hir, def_id::DefId};
 use rustc_lint::LateContext;
 
-use crate::utils::{match_def_path, paths, span_lint_and_help};
-
 use super::LINKEDLIST;
 
 pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, def_id: DefId) -> bool {
index 4a1a608e8ae62ea308ffa0f7dcb43e6f3b2129b0..c73c1c9d92db65b27e12ab9fc39995bf37d68319 100644 (file)
 use std::cmp::Ordering;
 use std::collections::BTreeMap;
 
+use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_help, span_lint_and_then};
+use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::ty::{is_isize_or_usize, is_type_diagnostic_item};
 use if_chain::if_chain;
-use rustc_errors::{Applicability, DiagnosticBuilder};
+use rustc_errors::DiagnosticBuilder;
 use rustc_hir as hir;
 use rustc_hir::intravisit::{walk_body, walk_expr, walk_ty, FnKind, NestedVisitorMap, Visitor};
 use rustc_hir::{
-    BinOpKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem,
-    ImplItemKind, Item, ItemKind, Local, MatchSource, MutTy, Node, QPath, Stmt, StmtKind, TraitFn, TraitItem,
-    TraitItemKind, TyKind,
+    BinOpKind, Body, Expr, ExprKind, FnDecl, FnRetTy, FnSig, GenericArg, GenericParamKind, HirId, ImplItem,
+    ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitFn, TraitItem, TraitItemKind, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::map::Map;
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, IntTy, Ty, TyS, TypeckResults, UintTy};
 use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
-use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::source_map::Span;
 use rustc_span::symbol::sym;
 use rustc_target::abi::LayoutOf;
 use rustc_typeck::hir_ty_to_ty;
 
 use crate::consts::{constant, Constant};
-use crate::utils::paths;
-use crate::utils::{
-    clip, comparisons, differing_macro_contexts, higher, indent_of, int_bits, is_isize_or_usize,
-    is_type_diagnostic_item, match_path, multispan_sugg, reindent_multiline, sext, snippet, snippet_opt,
-    snippet_with_macro_callsite, span_lint, span_lint_and_help, span_lint_and_then, unsext,
-};
+use clippy_utils::paths;
+use clippy_utils::{clip, comparisons, differing_macro_contexts, int_bits, match_path, sext, unsext};
 
 declare_clippy_lint! {
     /// **What it does:** Checks for use of `Box<Vec<_>>` anywhere in the code.
@@ -279,7 +276,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
         match item.kind {
             TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_ty(cx, ty, false),
             TraitItemKind::Fn(ref sig, _) => self.check_fn_decl(cx, &sig.decl),
-            _ => (),
+            TraitItemKind::Type(..) => (),
         }
     }
 
@@ -390,390 +387,6 @@ fn check_ty(&mut self, cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, is_local: boo
     }
 }
 
-declare_clippy_lint! {
-    /// **What it does:** Checks for binding a unit value.
-    ///
-    /// **Why is this bad?** A unit value cannot usefully be used anywhere. So
-    /// binding one is kind of pointless.
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    /// ```rust
-    /// let x = {
-    ///     1;
-    /// };
-    /// ```
-    pub LET_UNIT_VALUE,
-    pedantic,
-    "creating a `let` binding to a value of unit type, which usually can't be used afterwards"
-}
-
-declare_lint_pass!(LetUnitValue => [LET_UNIT_VALUE]);
-
-impl<'tcx> LateLintPass<'tcx> for LetUnitValue {
-    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
-        if let StmtKind::Local(ref local) = stmt.kind {
-            if is_unit(cx.typeck_results().pat_ty(&local.pat)) {
-                if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() {
-                    return;
-                }
-                if higher::is_from_for_desugar(local) {
-                    return;
-                }
-                span_lint_and_then(
-                    cx,
-                    LET_UNIT_VALUE,
-                    stmt.span,
-                    "this let-binding has unit value",
-                    |diag| {
-                        if let Some(expr) = &local.init {
-                            let snip = snippet_with_macro_callsite(cx, expr.span, "()");
-                            diag.span_suggestion(
-                                stmt.span,
-                                "omit the `let` binding",
-                                format!("{};", snip),
-                                Applicability::MachineApplicable, // snippet
-                            );
-                        }
-                    },
-                );
-            }
-        }
-    }
-}
-
-declare_clippy_lint! {
-    /// **What it does:** Checks for comparisons to unit. This includes all binary
-    /// comparisons (like `==` and `<`) and asserts.
-    ///
-    /// **Why is this bad?** Unit is always equal to itself, and thus is just a
-    /// clumsily written constant. Mostly this happens when someone accidentally
-    /// adds semicolons at the end of the operands.
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    /// ```rust
-    /// # fn foo() {};
-    /// # fn bar() {};
-    /// # fn baz() {};
-    /// if {
-    ///     foo();
-    /// } == {
-    ///     bar();
-    /// } {
-    ///     baz();
-    /// }
-    /// ```
-    /// is equal to
-    /// ```rust
-    /// # fn foo() {};
-    /// # fn bar() {};
-    /// # fn baz() {};
-    /// {
-    ///     foo();
-    ///     bar();
-    ///     baz();
-    /// }
-    /// ```
-    ///
-    /// For asserts:
-    /// ```rust
-    /// # fn foo() {};
-    /// # fn bar() {};
-    /// assert_eq!({ foo(); }, { bar(); });
-    /// ```
-    /// will always succeed
-    pub UNIT_CMP,
-    correctness,
-    "comparing unit values"
-}
-
-declare_lint_pass!(UnitCmp => [UNIT_CMP]);
-
-impl<'tcx> LateLintPass<'tcx> for UnitCmp {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if expr.span.from_expansion() {
-            if let Some(callee) = expr.span.source_callee() {
-                if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind {
-                    if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind {
-                        let op = cmp.node;
-                        if op.is_comparison() && is_unit(cx.typeck_results().expr_ty(left)) {
-                            let result = match &*symbol.as_str() {
-                                "assert_eq" | "debug_assert_eq" => "succeed",
-                                "assert_ne" | "debug_assert_ne" => "fail",
-                                _ => return,
-                            };
-                            span_lint(
-                                cx,
-                                UNIT_CMP,
-                                expr.span,
-                                &format!(
-                                    "`{}` of unit values detected. This will always {}",
-                                    symbol.as_str(),
-                                    result
-                                ),
-                            );
-                        }
-                    }
-                }
-            }
-            return;
-        }
-        if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind {
-            let op = cmp.node;
-            if op.is_comparison() && is_unit(cx.typeck_results().expr_ty(left)) {
-                let result = match op {
-                    BinOpKind::Eq | BinOpKind::Le | BinOpKind::Ge => "true",
-                    _ => "false",
-                };
-                span_lint(
-                    cx,
-                    UNIT_CMP,
-                    expr.span,
-                    &format!(
-                        "{}-comparison of unit values detected. This will always be {}",
-                        op.as_str(),
-                        result
-                    ),
-                );
-            }
-        }
-    }
-}
-
-declare_clippy_lint! {
-    /// **What it does:** Checks for passing a unit value as an argument to a function without using a
-    /// unit literal (`()`).
-    ///
-    /// **Why is this bad?** This is likely the result of an accidental semicolon.
-    ///
-    /// **Known problems:** None.
-    ///
-    /// **Example:**
-    /// ```rust,ignore
-    /// foo({
-    ///     let a = bar();
-    ///     baz(a);
-    /// })
-    /// ```
-    pub UNIT_ARG,
-    complexity,
-    "passing unit to a function"
-}
-
-declare_lint_pass!(UnitArg => [UNIT_ARG]);
-
-impl<'tcx> LateLintPass<'tcx> for UnitArg {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if expr.span.from_expansion() {
-            return;
-        }
-
-        // apparently stuff in the desugaring of `?` can trigger this
-        // so check for that here
-        // only the calls to `Try::from_error` is marked as desugared,
-        // so we need to check both the current Expr and its parent.
-        if is_questionmark_desugar_marked_call(expr) {
-            return;
-        }
-        if_chain! {
-            let map = &cx.tcx.hir();
-            let opt_parent_node = map.find(map.get_parent_node(expr.hir_id));
-            if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node;
-            if is_questionmark_desugar_marked_call(parent_expr);
-            then {
-                return;
-            }
-        }
-
-        match expr.kind {
-            ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => {
-                let args_to_recover = args
-                    .iter()
-                    .filter(|arg| {
-                        if is_unit(cx.typeck_results().expr_ty(arg)) && !is_unit_literal(arg) {
-                            !matches!(
-                                &arg.kind,
-                                ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..)
-                            )
-                        } else {
-                            false
-                        }
-                    })
-                    .collect::<Vec<_>>();
-                if !args_to_recover.is_empty() {
-                    lint_unit_args(cx, expr, &args_to_recover);
-                }
-            },
-            _ => (),
-        }
-    }
-}
-
-fn fmt_stmts_and_call(
-    cx: &LateContext<'_>,
-    call_expr: &Expr<'_>,
-    call_snippet: &str,
-    args_snippets: &[impl AsRef<str>],
-    non_empty_block_args_snippets: &[impl AsRef<str>],
-) -> String {
-    let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0);
-    let call_snippet_with_replacements = args_snippets
-        .iter()
-        .fold(call_snippet.to_owned(), |acc, arg| acc.replacen(arg.as_ref(), "()", 1));
-
-    let mut stmts_and_call = non_empty_block_args_snippets
-        .iter()
-        .map(|it| it.as_ref().to_owned())
-        .collect::<Vec<_>>();
-    stmts_and_call.push(call_snippet_with_replacements);
-    stmts_and_call = stmts_and_call
-        .into_iter()
-        .map(|v| reindent_multiline(v.into(), true, Some(call_expr_indent)).into_owned())
-        .collect();
-
-    let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent)));
-    // expr is not in a block statement or result expression position, wrap in a block
-    let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(call_expr.hir_id));
-    if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) {
-        let block_indent = call_expr_indent + 4;
-        stmts_and_call_snippet =
-            reindent_multiline(stmts_and_call_snippet.into(), true, Some(block_indent)).into_owned();
-        stmts_and_call_snippet = format!(
-            "{{\n{}{}\n{}}}",
-            " ".repeat(block_indent),
-            &stmts_and_call_snippet,
-            " ".repeat(call_expr_indent)
-        );
-    }
-    stmts_and_call_snippet
-}
-
-fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) {
-    let mut applicability = Applicability::MachineApplicable;
-    let (singular, plural) = if args_to_recover.len() > 1 {
-        ("", "s")
-    } else {
-        ("a ", "")
-    };
-    span_lint_and_then(
-        cx,
-        UNIT_ARG,
-        expr.span,
-        &format!("passing {}unit value{} to a function", singular, plural),
-        |db| {
-            let mut or = "";
-            args_to_recover
-                .iter()
-                .filter_map(|arg| {
-                    if_chain! {
-                        if let ExprKind::Block(block, _) = arg.kind;
-                        if block.expr.is_none();
-                        if let Some(last_stmt) = block.stmts.iter().last();
-                        if let StmtKind::Semi(last_expr) = last_stmt.kind;
-                        if let Some(snip) = snippet_opt(cx, last_expr.span);
-                        then {
-                            Some((
-                                last_stmt.span,
-                                snip,
-                            ))
-                        }
-                        else {
-                            None
-                        }
-                    }
-                })
-                .for_each(|(span, sugg)| {
-                    db.span_suggestion(
-                        span,
-                        "remove the semicolon from the last statement in the block",
-                        sugg,
-                        Applicability::MaybeIncorrect,
-                    );
-                    or = "or ";
-                    applicability = Applicability::MaybeIncorrect;
-                });
-
-            let arg_snippets: Vec<String> = args_to_recover
-                .iter()
-                .filter_map(|arg| snippet_opt(cx, arg.span))
-                .collect();
-            let arg_snippets_without_empty_blocks: Vec<String> = args_to_recover
-                .iter()
-                .filter(|arg| !is_empty_block(arg))
-                .filter_map(|arg| snippet_opt(cx, arg.span))
-                .collect();
-
-            if let Some(call_snippet) = snippet_opt(cx, expr.span) {
-                let sugg = fmt_stmts_and_call(
-                    cx,
-                    expr,
-                    &call_snippet,
-                    &arg_snippets,
-                    &arg_snippets_without_empty_blocks,
-                );
-
-                if arg_snippets_without_empty_blocks.is_empty() {
-                    db.multipart_suggestion(
-                        &format!("use {}unit literal{} instead", singular, plural),
-                        args_to_recover
-                            .iter()
-                            .map(|arg| (arg.span, "()".to_string()))
-                            .collect::<Vec<_>>(),
-                        applicability,
-                    );
-                } else {
-                    let plural = arg_snippets_without_empty_blocks.len() > 1;
-                    let empty_or_s = if plural { "s" } else { "" };
-                    let it_or_them = if plural { "them" } else { "it" };
-                    db.span_suggestion(
-                        expr.span,
-                        &format!(
-                            "{}move the expression{} in front of the call and replace {} with the unit literal `()`",
-                            or, empty_or_s, it_or_them
-                        ),
-                        sugg,
-                        applicability,
-                    );
-                }
-            }
-        },
-    );
-}
-
-fn is_empty_block(expr: &Expr<'_>) -> bool {
-    matches!(
-        expr.kind,
-        ExprKind::Block(
-            Block {
-                stmts: &[],
-                expr: None,
-                ..
-            },
-            _,
-        )
-    )
-}
-
-fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool {
-    use rustc_span::hygiene::DesugaringKind;
-    if let ExprKind::Call(ref callee, _) = expr.kind {
-        callee.span.is_desugaring(DesugaringKind::QuestionMark)
-    } else {
-        false
-    }
-}
-
-fn is_unit(ty: Ty<'_>) -> bool {
-    matches!(ty.kind(), ty::Tuple(slice) if slice.is_empty())
-}
-
-fn is_unit_literal(expr: &Expr<'_>) -> bool {
-    matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty())
-}
-
 declare_clippy_lint! {
     /// **What it does:** Checks for types used in structs, parameters and `let`
     /// declarations above a certain complexity threshold.
@@ -839,7 +452,7 @@ fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>
             TraitItemKind::Const(ref ty, _) | TraitItemKind::Type(_, Some(ref ty)) => self.check_type(cx, ty),
             TraitItemKind::Fn(FnSig { ref decl, .. }, TraitFn::Required(_)) => self.check_fndecl(cx, decl),
             // methods with default impl are covered by check_fn
-            _ => (),
+            TraitItemKind::Type(..) | TraitItemKind::Fn(_, TraitFn::Provided(_)) => (),
         }
     }
 
@@ -847,7 +460,7 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>)
         match item.kind {
             ImplItemKind::Const(ref ty, _) | ImplItemKind::TyAlias(ref ty) => self.check_type(cx, ty),
             // methods are covered by check_fn
-            _ => (),
+            ImplItemKind::Fn(..) => (),
         }
     }
 
@@ -911,7 +524,7 @@ fn visit_ty(&mut self, ty: &'tcx hir::Ty<'_>) {
             // function types bring a lot of overhead
             TyKind::BareFn(ref bare) if bare.abi == Abi::Rust => (50 * self.nest, 1),
 
-            TyKind::TraitObject(ref param_bounds, ..) => {
+            TyKind::TraitObject(ref param_bounds, _, _) => {
                 let has_lifetime_parameters = param_bounds.iter().any(|bound| {
                     bound
                         .bound_generic_params
@@ -1005,7 +618,7 @@ fn detect_absurd_comparison<'tcx>(
 ) -> Option<(ExtremeExpr<'tcx>, AbsurdComparisonResult)> {
     use crate::types::AbsurdComparisonResult::{AlwaysFalse, AlwaysTrue, InequalityImpossible};
     use crate::types::ExtremeType::{Maximum, Minimum};
-    use crate::utils::comparisons::{normalize_comparison, Rel};
+    use clippy_utils::comparisons::{normalize_comparison, Rel};
 
     // absurd comparison only makes sense on primitive types
     // primitive types don't implement comparison operators with each other
@@ -1247,7 +860,7 @@ fn upcast_comparison_bounds_err<'tcx>(
     rhs: &'tcx Expr<'_>,
     invert: bool,
 ) {
-    use crate::utils::comparisons::Rel;
+    use clippy_utils::comparisons::Rel;
 
     if let Some((lb, ub)) = lhs_bounds {
         if let Some(norm_rhs_val) = node_as_const_fullint(cx, rhs) {
index dc5db963b4e98a98a4a52294c4ad4dc0d46d5ff3..b2692c48076b64288d01e8cc646f1c2e5b4fca9b 100644 (file)
@@ -1,9 +1,9 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_ty_param_diagnostic_item;
 use rustc_hir::{self as hir, def_id::DefId, QPath};
 use rustc_lint::LateContext;
 use rustc_span::symbol::sym;
 
-use crate::utils::{is_ty_param_diagnostic_item, span_lint};
-
 use super::OPTION_OPTION;
 
 pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
index e34b95147e10a6d4cf2fb2c06734b9d1459a71d2..ef629a35d107eb383b8af192ac92fa166be4cedf 100644 (file)
@@ -1,12 +1,11 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item};
 use rustc_errors::Applicability;
 use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind};
 use rustc_lint::LateContext;
 use rustc_span::symbol::sym;
 
-use crate::utils::{
-    get_qpath_generic_tys, is_ty_param_diagnostic_item, snippet_with_applicability, span_lint_and_sugg,
-};
-
 use super::RC_BUFFER;
 
 pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
index 5da6db179c46ee1a7e98a7ca614939865a4bbe96..c0c1f340583c1b53cb126407fe8ccb96c53aa9da 100644 (file)
@@ -1,13 +1,11 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item};
 use rustc_errors::Applicability;
 use rustc_hir::{self as hir, def_id::DefId, LangItem, QPath, TyKind};
 use rustc_lint::LateContext;
 use rustc_span::symbol::sym;
 
-use crate::utils::{
-    get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item, snippet_with_applicability,
-    span_lint_and_sugg,
-};
-
 use super::{utils, REDUNDANT_ALLOCATION};
 
 pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
index 4d64748f998a40deac3d0d667aba6a3d69f851e8..45f891ed71831c6dd635b2313c3b4b59a2c35579 100644 (file)
@@ -1,11 +1,9 @@
+use clippy_utils::last_path_segment;
+use if_chain::if_chain;
 use rustc_hir::{GenericArg, QPath, TyKind};
 use rustc_lint::LateContext;
 use rustc_span::source_map::Span;
 
-use crate::utils::last_path_segment;
-
-use if_chain::if_chain;
-
 pub(super) fn match_borrows_parameter(_cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Span> {
     let last = last_path_segment(qpath);
     if_chain! {
index 2530cc133c6784ec04da443d68894b6b5b2bc0fd..d2c373db261d96ffff660b7fd8efb21723dd2d2b 100644 (file)
@@ -1,3 +1,7 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::last_path_segment;
+use clippy_utils::source::snippet;
+use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{self as hir, def_id::DefId, GenericArg, QPath, TyKind};
 use rustc_lint::LateContext;
@@ -6,10 +10,6 @@
 use rustc_target::abi::LayoutOf;
 use rustc_typeck::hir_ty_to_ty;
 
-use if_chain::if_chain;
-
-use crate::utils::{last_path_segment, snippet, span_lint_and_sugg};
-
 use super::VEC_BOX;
 
 pub(super) fn check(
index 5443f1601fcbb083c8fcef94219a56a26d67cc5c..b6749069176b867ffc630119d1aa4bb7b99861b9 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{is_type_lang_item, match_function_call, paths, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::ty::is_type_lang_item;
+use clippy_utils::{match_function_call, paths};
 use rustc_hir::{lang_items, Expr};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
index 93d59cc7fcd178bf42c1fae30b8626206e29fa82..d81e31f5a21d46000662efbe17faada7b1f47283 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{is_allowed, snippet, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_allowed;
+use clippy_utils::source::snippet;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, HirId};
index c6ae8b9b59837f608bdf9a531b19f6473d6abe0c..bad3e488bb6d501571d3f6c47438c3cc38db966a 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{get_trait_def_id, paths, span_lint, span_lint_and_help};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::{get_trait_def_id, paths};
 use if_chain::if_chain;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{Expr, ExprKind, StmtKind};
diff --git a/clippy_lints/src/unit_types/let_unit_value.rs b/clippy_lints/src/unit_types/let_unit_value.rs
new file mode 100644 (file)
index 0000000..8698a71
--- /dev/null
@@ -0,0 +1,39 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::higher;
+use clippy_utils::source::snippet_with_macro_callsite;
+use rustc_errors::Applicability;
+use rustc_hir::{Stmt, StmtKind};
+use rustc_lint::{LateContext, LintContext};
+use rustc_middle::lint::in_external_macro;
+
+use super::LET_UNIT_VALUE;
+
+pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
+    if let StmtKind::Local(ref local) = stmt.kind {
+        if cx.typeck_results().pat_ty(&local.pat).is_unit() {
+            if in_external_macro(cx.sess(), stmt.span) || local.pat.span.from_expansion() {
+                return;
+            }
+            if higher::is_from_for_desugar(local) {
+                return;
+            }
+            span_lint_and_then(
+                cx,
+                LET_UNIT_VALUE,
+                stmt.span,
+                "this let-binding has unit value",
+                |diag| {
+                    if let Some(expr) = &local.init {
+                        let snip = snippet_with_macro_callsite(cx, expr.span, "()");
+                        diag.span_suggestion(
+                            stmt.span,
+                            "omit the `let` binding",
+                            format!("{};", snip),
+                            Applicability::MachineApplicable, // snippet
+                        );
+                    }
+                },
+            );
+        }
+    }
+}
diff --git a/clippy_lints/src/unit_types/mod.rs b/clippy_lints/src/unit_types/mod.rs
new file mode 100644 (file)
index 0000000..64420a0
--- /dev/null
@@ -0,0 +1,107 @@
+mod let_unit_value;
+mod unit_arg;
+mod unit_cmp;
+mod utils;
+
+use rustc_hir::{Expr, Stmt};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for binding a unit value.
+    ///
+    /// **Why is this bad?** A unit value cannot usefully be used anywhere. So
+    /// binding one is kind of pointless.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// let x = {
+    ///     1;
+    /// };
+    /// ```
+    pub LET_UNIT_VALUE,
+    pedantic,
+    "creating a `let` binding to a value of unit type, which usually can't be used afterwards"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for comparisons to unit. This includes all binary
+    /// comparisons (like `==` and `<`) and asserts.
+    ///
+    /// **Why is this bad?** Unit is always equal to itself, and thus is just a
+    /// clumsily written constant. Mostly this happens when someone accidentally
+    /// adds semicolons at the end of the operands.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust
+    /// # fn foo() {};
+    /// # fn bar() {};
+    /// # fn baz() {};
+    /// if {
+    ///     foo();
+    /// } == {
+    ///     bar();
+    /// } {
+    ///     baz();
+    /// }
+    /// ```
+    /// is equal to
+    /// ```rust
+    /// # fn foo() {};
+    /// # fn bar() {};
+    /// # fn baz() {};
+    /// {
+    ///     foo();
+    ///     bar();
+    ///     baz();
+    /// }
+    /// ```
+    ///
+    /// For asserts:
+    /// ```rust
+    /// # fn foo() {};
+    /// # fn bar() {};
+    /// assert_eq!({ foo(); }, { bar(); });
+    /// ```
+    /// will always succeed
+    pub UNIT_CMP,
+    correctness,
+    "comparing unit values"
+}
+
+declare_clippy_lint! {
+    /// **What it does:** Checks for passing a unit value as an argument to a function without using a
+    /// unit literal (`()`).
+    ///
+    /// **Why is this bad?** This is likely the result of an accidental semicolon.
+    ///
+    /// **Known problems:** None.
+    ///
+    /// **Example:**
+    /// ```rust,ignore
+    /// foo({
+    ///     let a = bar();
+    ///     baz(a);
+    /// })
+    /// ```
+    pub UNIT_ARG,
+    complexity,
+    "passing unit to a function"
+}
+
+declare_lint_pass!(UnitTypes => [LET_UNIT_VALUE, UNIT_CMP, UNIT_ARG]);
+
+impl LateLintPass<'_> for UnitTypes {
+    fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
+        let_unit_value::check(cx, stmt);
+    }
+
+    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
+        unit_cmp::check(cx, expr);
+        unit_arg::check(cx, expr);
+    }
+}
diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs
new file mode 100644 (file)
index 0000000..925ab57
--- /dev/null
@@ -0,0 +1,207 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{self as hir, Block, Expr, ExprKind, MatchSource, Node, StmtKind};
+use rustc_lint::LateContext;
+
+use super::{utils, UNIT_ARG};
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
+    if expr.span.from_expansion() {
+        return;
+    }
+
+    // apparently stuff in the desugaring of `?` can trigger this
+    // so check for that here
+    // only the calls to `Try::from_error` is marked as desugared,
+    // so we need to check both the current Expr and its parent.
+    if is_questionmark_desugar_marked_call(expr) {
+        return;
+    }
+    if_chain! {
+        let map = &cx.tcx.hir();
+        let opt_parent_node = map.find(map.get_parent_node(expr.hir_id));
+        if let Some(hir::Node::Expr(parent_expr)) = opt_parent_node;
+        if is_questionmark_desugar_marked_call(parent_expr);
+        then {
+            return;
+        }
+    }
+
+    match expr.kind {
+        ExprKind::Call(_, args) | ExprKind::MethodCall(_, _, args, _) => {
+            let args_to_recover = args
+                .iter()
+                .filter(|arg| {
+                    if cx.typeck_results().expr_ty(arg).is_unit() && !utils::is_unit_literal(arg) {
+                        !matches!(
+                            &arg.kind,
+                            ExprKind::Match(.., MatchSource::TryDesugar) | ExprKind::Path(..)
+                        )
+                    } else {
+                        false
+                    }
+                })
+                .collect::<Vec<_>>();
+            if !args_to_recover.is_empty() {
+                lint_unit_args(cx, expr, &args_to_recover);
+            }
+        },
+        _ => (),
+    }
+}
+
+fn is_questionmark_desugar_marked_call(expr: &Expr<'_>) -> bool {
+    use rustc_span::hygiene::DesugaringKind;
+    if let ExprKind::Call(ref callee, _) = expr.kind {
+        callee.span.is_desugaring(DesugaringKind::QuestionMark)
+    } else {
+        false
+    }
+}
+
+fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Expr<'_>]) {
+    let mut applicability = Applicability::MachineApplicable;
+    let (singular, plural) = if args_to_recover.len() > 1 {
+        ("", "s")
+    } else {
+        ("a ", "")
+    };
+    span_lint_and_then(
+        cx,
+        UNIT_ARG,
+        expr.span,
+        &format!("passing {}unit value{} to a function", singular, plural),
+        |db| {
+            let mut or = "";
+            args_to_recover
+                .iter()
+                .filter_map(|arg| {
+                    if_chain! {
+                        if let ExprKind::Block(block, _) = arg.kind;
+                        if block.expr.is_none();
+                        if let Some(last_stmt) = block.stmts.iter().last();
+                        if let StmtKind::Semi(last_expr) = last_stmt.kind;
+                        if let Some(snip) = snippet_opt(cx, last_expr.span);
+                        then {
+                            Some((
+                                last_stmt.span,
+                                snip,
+                            ))
+                        }
+                        else {
+                            None
+                        }
+                    }
+                })
+                .for_each(|(span, sugg)| {
+                    db.span_suggestion(
+                        span,
+                        "remove the semicolon from the last statement in the block",
+                        sugg,
+                        Applicability::MaybeIncorrect,
+                    );
+                    or = "or ";
+                    applicability = Applicability::MaybeIncorrect;
+                });
+
+            let arg_snippets: Vec<String> = args_to_recover
+                .iter()
+                .filter_map(|arg| snippet_opt(cx, arg.span))
+                .collect();
+            let arg_snippets_without_empty_blocks: Vec<String> = args_to_recover
+                .iter()
+                .filter(|arg| !is_empty_block(arg))
+                .filter_map(|arg| snippet_opt(cx, arg.span))
+                .collect();
+
+            if let Some(call_snippet) = snippet_opt(cx, expr.span) {
+                let sugg = fmt_stmts_and_call(
+                    cx,
+                    expr,
+                    &call_snippet,
+                    &arg_snippets,
+                    &arg_snippets_without_empty_blocks,
+                );
+
+                if arg_snippets_without_empty_blocks.is_empty() {
+                    db.multipart_suggestion(
+                        &format!("use {}unit literal{} instead", singular, plural),
+                        args_to_recover
+                            .iter()
+                            .map(|arg| (arg.span, "()".to_string()))
+                            .collect::<Vec<_>>(),
+                        applicability,
+                    );
+                } else {
+                    let plural = arg_snippets_without_empty_blocks.len() > 1;
+                    let empty_or_s = if plural { "s" } else { "" };
+                    let it_or_them = if plural { "them" } else { "it" };
+                    db.span_suggestion(
+                        expr.span,
+                        &format!(
+                            "{}move the expression{} in front of the call and replace {} with the unit literal `()`",
+                            or, empty_or_s, it_or_them
+                        ),
+                        sugg,
+                        applicability,
+                    );
+                }
+            }
+        },
+    );
+}
+
+fn is_empty_block(expr: &Expr<'_>) -> bool {
+    matches!(
+        expr.kind,
+        ExprKind::Block(
+            Block {
+                stmts: &[],
+                expr: None,
+                ..
+            },
+            _,
+        )
+    )
+}
+
+fn fmt_stmts_and_call(
+    cx: &LateContext<'_>,
+    call_expr: &Expr<'_>,
+    call_snippet: &str,
+    args_snippets: &[impl AsRef<str>],
+    non_empty_block_args_snippets: &[impl AsRef<str>],
+) -> String {
+    let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0);
+    let call_snippet_with_replacements = args_snippets
+        .iter()
+        .fold(call_snippet.to_owned(), |acc, arg| acc.replacen(arg.as_ref(), "()", 1));
+
+    let mut stmts_and_call = non_empty_block_args_snippets
+        .iter()
+        .map(|it| it.as_ref().to_owned())
+        .collect::<Vec<_>>();
+    stmts_and_call.push(call_snippet_with_replacements);
+    stmts_and_call = stmts_and_call
+        .into_iter()
+        .map(|v| reindent_multiline(v.into(), true, Some(call_expr_indent)).into_owned())
+        .collect();
+
+    let mut stmts_and_call_snippet = stmts_and_call.join(&format!("{}{}", ";\n", " ".repeat(call_expr_indent)));
+    // expr is not in a block statement or result expression position, wrap in a block
+    let parent_node = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(call_expr.hir_id));
+    if !matches!(parent_node, Some(Node::Block(_))) && !matches!(parent_node, Some(Node::Stmt(_))) {
+        let block_indent = call_expr_indent + 4;
+        stmts_and_call_snippet =
+            reindent_multiline(stmts_and_call_snippet.into(), true, Some(block_indent)).into_owned();
+        stmts_and_call_snippet = format!(
+            "{{\n{}{}\n{}}}",
+            " ".repeat(block_indent),
+            &stmts_and_call_snippet,
+            " ".repeat(call_expr_indent)
+        );
+    }
+    stmts_and_call_snippet
+}
diff --git a/clippy_lints/src/unit_types/unit_cmp.rs b/clippy_lints/src/unit_types/unit_cmp.rs
new file mode 100644 (file)
index 0000000..b3077de
--- /dev/null
@@ -0,0 +1,56 @@
+use clippy_utils::diagnostics::span_lint;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::hygiene::{ExpnKind, MacroKind};
+
+use super::UNIT_CMP;
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
+    if expr.span.from_expansion() {
+        if let Some(callee) = expr.span.source_callee() {
+            if let ExpnKind::Macro(MacroKind::Bang, symbol) = callee.kind {
+                if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind {
+                    let op = cmp.node;
+                    if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() {
+                        let result = match &*symbol.as_str() {
+                            "assert_eq" | "debug_assert_eq" => "succeed",
+                            "assert_ne" | "debug_assert_ne" => "fail",
+                            _ => return,
+                        };
+                        span_lint(
+                            cx,
+                            UNIT_CMP,
+                            expr.span,
+                            &format!(
+                                "`{}` of unit values detected. This will always {}",
+                                symbol.as_str(),
+                                result
+                            ),
+                        );
+                    }
+                }
+            }
+        }
+        return;
+    }
+
+    if let ExprKind::Binary(ref cmp, ref left, _) = expr.kind {
+        let op = cmp.node;
+        if op.is_comparison() && cx.typeck_results().expr_ty(left).is_unit() {
+            let result = match op {
+                BinOpKind::Eq | BinOpKind::Le | BinOpKind::Ge => "true",
+                _ => "false",
+            };
+            span_lint(
+                cx,
+                UNIT_CMP,
+                expr.span,
+                &format!(
+                    "{}-comparison of unit values detected. This will always be {}",
+                    op.as_str(),
+                    result
+                ),
+            );
+        }
+    }
+}
diff --git a/clippy_lints/src/unit_types/utils.rs b/clippy_lints/src/unit_types/utils.rs
new file mode 100644 (file)
index 0000000..4e194a0
--- /dev/null
@@ -0,0 +1,5 @@
+use rustc_hir::{Expr, ExprKind};
+
+pub(super) fn is_unit_literal(expr: &Expr<'_>) -> bool {
+    matches!(expr.kind, ExprKind::Tup(ref slice) if slice.is_empty())
+}
index 9582c162e77b274734c7e976bc1e17735fde48f5..d5bc3de669846325cabecd1163b8ac5073c8d2b8 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{match_def_path, paths, span_lint, span_lint_and_help};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
index 00a707107bce91f8404057d367c1fe74d757879d..e23bab5eba03fc2f50f16f23e2a9935176875aaf 100644 (file)
@@ -1,5 +1,6 @@
-use crate::utils;
-use crate::utils::sugg::Sugg;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Mutability, Param, Pat, PatKind, Path, PathSegment, QPath};
@@ -176,7 +177,7 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<LintTrigger> {
         if let name = name_ident.ident.name.to_ident_string();
         if name == "sort_by" || name == "sort_unstable_by";
         if let [vec, Expr { kind: ExprKind::Closure(_, _, closure_body_id, _, _), .. }] = args;
-        if utils::is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::vec_type);
+        if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(vec), sym::vec_type);
         if let closure_body = cx.tcx.hir().body(*closure_body_id);
         if let &[
             Param { pat: Pat { kind: PatKind::Binding(_, _, left_ident, _), .. }, ..},
@@ -232,7 +233,7 @@ fn expr_borrows(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 impl LateLintPass<'_> for UnnecessarySortBy {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
         match detect_lint(cx, expr) {
-            Some(LintTrigger::SortByKey(trigger)) => utils::span_lint_and_sugg(
+            Some(LintTrigger::SortByKey(trigger)) => span_lint_and_sugg(
                 cx,
                 UNNECESSARY_SORT_BY,
                 expr.span,
@@ -255,7 +256,7 @@ fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
                     Applicability::MachineApplicable
                 },
             ),
-            Some(LintTrigger::Sort(trigger)) => utils::span_lint_and_sugg(
+            Some(LintTrigger::Sort(trigger)) => span_lint_and_sugg(
                 cx,
                 UNNECESSARY_SORT_BY,
                 expr.span,
index 8e076397c119a9a25f94f16734d979f6a593790d..c2be457e9dcf96066544102f1e6620c47466d3a2 100644 (file)
@@ -1,7 +1,6 @@
-use crate::utils::{
-    contains_return, in_macro, match_qpath, paths, return_ty, snippet, span_lint_and_then,
-    visitors::find_all_ret_expressions,
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::{contains_return, in_macro, match_qpath, paths, return_ty, visitors::find_all_ret_expressions};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
@@ -72,7 +71,7 @@ fn check_fn(
                 }
             },
             FnKind::Closure => return,
-            _ => (),
+            FnKind::Method(..) => (),
         }
 
         // Abort if the method is implementing a trait or of it a trait method.
index 5826e9a4aa5b354a8023eb3f6ffa9910d5f18e5c..2b9479365c67d3659bc09accbdaf9dd4b33f671d 100644 (file)
@@ -1,7 +1,8 @@
 #![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
 
-use crate::utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path};
-use crate::utils::{over, span_lint_and_then};
+use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_pat, eq_path};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::over;
 use rustc_ast::mut_visit::*;
 use rustc_ast::ptr::P;
 use rustc_ast::{self as ast, Pat, PatKind, PatKind::*, DUMMY_NODE_ID};
index 154082a0fdb530b8e81d1d4e47f3abced3f96b6b..16ad9d2dfd32c6ba9517fa387d7cce7d3441bf9b 100644 (file)
@@ -1,4 +1,4 @@
-use crate::utils::span_lint;
+use clippy_utils::diagnostics::span_lint;
 use rustc_ast::ast::{Item, ItemKind, UseTree, UseTreeKind};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
index 43166d26787a71258ab4c615e4d641f5b4ed50f1..9990052e114b1f1e08426c0a88a98c0abd1801f4 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{is_try, match_trait_method, paths, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{is_try, match_trait_method, paths};
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
index 812482cf5cfb6638bdbb7eeaa18461c6937a8467..aef4ce7591596f1b01dc6c6d977c83b95d1f71c6 100644 (file)
@@ -1,11 +1,10 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::visitors::LocalUsedVisitor;
 use if_chain::if_chain;
 use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 
-use crate::utils::span_lint_and_help;
-use crate::utils::visitors::LocalUsedVisitor;
-
 declare_clippy_lint! {
     /// **What it does:** Checks methods that contain a `self` argument but don't use it
     ///
index a31cd5fda849ed918211bf6d6822cf8555b3954c..329ea49024bc018a0176b6f08d5a4407ca8e6122 100644 (file)
@@ -1,3 +1,5 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::position_before_rarrow;
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_ast::visit::FnKind;
@@ -7,8 +9,6 @@
 use rustc_span::source_map::Span;
 use rustc_span::BytePos;
 
-use crate::utils::{position_before_rarrow, span_lint_and_sugg};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for unit (`()`) expressions that can be removed.
     ///
index 2fb0463c5a6c250c646ceb5cdcd2e795f5c39df4..fb29acca18a98333cffa89589ace01a2d26d5fee 100644 (file)
@@ -1,6 +1,6 @@
-use crate::utils::{
-    differing_macro_contexts, is_type_diagnostic_item, span_lint_and_then, usage::is_potentially_mutated,
-};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{differing_macro_contexts, usage::is_potentially_mutated};
 use if_chain::if_chain;
 use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor};
 use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, HirId, Path, QPath, UnOp};
index 8cb7429849da65bf207d9221c5f6805f5aecd17b..0d745813beb782cce0cbf26c1ef3c2506a6317a7 100644 (file)
@@ -1,6 +1,10 @@
-use crate::utils::{is_type_diagnostic_item, method_chain_args, return_ty, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{method_chain_args, return_ty};
 use if_chain::if_chain;
 use rustc_hir as hir;
+use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
+use rustc_hir::{Expr, ImplItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::map::Map;
 use rustc_middle::ty;
@@ -66,9 +70,6 @@ fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::Impl
     }
 }
 
-use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
-use rustc_hir::{Expr, ImplItemKind};
-
 struct FindExpectUnwrap<'a, 'tcx> {
     lcx: &'a LateContext<'tcx>,
     typeck_results: &'tcx ty::TypeckResults<'tcx>,
index 0470e1dbbb8122ae9804b2634f05986e9458a372..a6d29d3686218c11119c4d0dd94f7a0c7eefec95 100644 (file)
@@ -1,7 +1,7 @@
-use crate::utils::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_sugg;
 use if_chain::if_chain;
 use itertools::Itertools;
-use rustc_ast::ast::{Item, ItemKind, Variant};
+use rustc_ast::ast::{Item, ItemKind, Variant, VisibilityKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -81,7 +81,7 @@ fn check_ident(cx: &EarlyContext<'_>, ident: &Ident, be_aggressive: bool) {
     // assume that two-letter words are some kind of valid abbreviation like FP for false positive
     // (and don't warn)
     if (ident.chars().all(|c| c.is_ascii_uppercase()) && ident.len() > 2)
-    // otherwise, warn if we have SOmeTHING lIKE THIs but only warn with the aggressive 
+    // otherwise, warn if we have SOmeTHING lIKE THIs but only warn with the aggressive
     // upper-case-acronyms-aggressive config option enabled
     || (be_aggressive && ident != &corrected)
     {
@@ -105,6 +105,8 @@ fn check_item(&mut self, cx: &EarlyContext<'_>, it: &Item) {
                 it.kind,
                 ItemKind::TyAlias(..) | ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Trait(..)
             );
+            // do not lint public items
+            if !matches!(it.vis.kind, VisibilityKind::Public);
             then {
                 check_ident(cx, &it.ident, self.upper_case_acronyms_aggressive);
             }
index f0523cec6211d7a8eb74a5dec59288a4421a3663..116cb8b1e1c70e5232a2368f68d3c3fa9d22dd3c 100644 (file)
@@ -1,6 +1,7 @@
-use crate::utils::{in_macro, meets_msrv, snippet_opt, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::{in_macro, meets_msrv};
 use if_chain::if_chain;
-
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::def::DefKind;
index c53348539860524ce55b0e9db2fadfd55ca07314..3e1b69e676b955e9389cfbe13eff30c952d52142 100644 (file)
@@ -1,8 +1,8 @@
-use crate::utils::sugg::Sugg;
-use crate::utils::{
-    get_parent_expr, is_type_diagnostic_item, match_def_path, match_trait_method, paths, snippet,
-    snippet_with_macro_callsite, span_lint_and_help, span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::source::{snippet, snippet_with_macro_callsite};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{get_parent_expr, match_def_path, match_trait_method, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, HirId, MatchSource};
index c57614800805ea2ae2c9f52fe26b9edfc4607c75..a92c987014f195b946dd329aa8afcd6ff7298a9d 100644 (file)
@@ -1,7 +1,7 @@
 //! A group of attributes that can be attached to Rust code in order
 //! to generate a clippy lint detecting said code automatically.
 
-use crate::utils::get_attr;
+use clippy_utils::get_attr;
 use rustc_ast::ast::{LitFloatType, LitKind};
 use rustc_ast::walk_list;
 use rustc_data_structures::fx::FxHashMap;
index 64ee9e65bb1a6e16aa10ec7718afc511ac9c36d6..6fd3c9d7dec25856f661f2f26e3e73d0d7b7e6dd 100644 (file)
@@ -1,6 +1,6 @@
 //! checks for attributes
 
-use crate::utils::get_attr;
+use clippy_utils::get_attr;
 use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece};
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
index 0a347516c3ad2dd7e6e47ca04bd460d5c83e72c7..c496ff1fb24251039f39d3eca21c7b82204f881b 100644 (file)
@@ -1,8 +1,8 @@
 use crate::consts::{constant_simple, Constant};
-use crate::utils::{
-    is_expn_of, match_def_path, match_qpath, match_type, method_calls, path_to_res, paths, run_lints, snippet,
-    span_lint, span_lint_and_help, span_lint_and_sugg, SpanlessEq,
-};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::source::snippet;
+use clippy_utils::ty::match_type;
+use clippy_utils::{is_expn_of, match_def_path, match_qpath, method_calls, path_to_res, paths, run_lints, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::{Crate as AstCrate, ItemKind, LitKind, ModKind, NodeId};
 use rustc_ast::visit::FnKind;
index be9a07f8d7c9e10a900ef43d6d124720c6946d89..d8b31344e6d8bd68b91284c3a47dabfad3837b95 100644 (file)
@@ -3,5 +3,3 @@
 pub mod inspector;
 #[cfg(feature = "internal-lints")]
 pub mod internal_lints;
-
-pub use clippy_utils::*;
index c132e4de4f67ba450b877bf49fe09066bf5b32a8..1af9583887fbdfc1e71b1715b347768a8b4a8bbb 100644 (file)
@@ -1,6 +1,9 @@
 use crate::consts::{constant, Constant};
 use crate::rustc_target::abi::LayoutOf;
-use crate::utils::{higher, is_copy, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_copy;
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind};
index 8d111f98add9aac2c7ba145d443bbbf67cb5a594..8b696ed1c845d813aeed7468785e3b7c06697bb0 100644 (file)
@@ -1,6 +1,7 @@
-use crate::utils::{
-    is_type_diagnostic_item, match_def_path, path_to_local, path_to_local_id, paths, snippet, span_lint_and_sugg,
-};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{match_def_path, path_to_local, path_to_local_id, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
index d2494b321efcd48d6dc33142286a49d5e54e2adf..e035d3c5cadec9c5ef94c24bc209407245d6dc92 100644 (file)
@@ -1,15 +1,14 @@
-use crate::utils::span_lint_and_then;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{match_def_path, paths};
 use if_chain::if_chain;
+use rustc_ast::LitKind;
 use rustc_errors::Applicability;
+use rustc_hir as hir;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Spanned;
 
-use crate::utils::{match_def_path, paths};
-use rustc_ast::LitKind;
-use rustc_hir as hir;
-
 declare_clippy_lint! {
     /// **What it does:** Finds occurrences of `Vec::resize(0, an_int)`
     ///
index 32574d9d6c9a821fcf0f5db89339ba81e4829908..ec209b309513e0921aae95c6760e626f20e66b03 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{match_type, paths, span_lint_and_help};
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::paths;
+use clippy_utils::ty::match_type;
 use if_chain::if_chain;
 use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
index cd1864f461d3b284787ad22668339a502fe1107a..60c3489a449b6796714a416120985ec2e8a7f33d 100644 (file)
@@ -1,4 +1,5 @@
-use crate::utils::{run_lints, span_lint};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::run_lints;
 use rustc_hir::{hir_id::CRATE_HIR_ID, Crate};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
index 094b1a42346c23ff64444c560e087d1586fd16a3..51c1117d20641f92c2161cc1f87b7b7a6a8934fb 100644 (file)
@@ -1,4 +1,6 @@
-use crate::utils::{in_macro, snippet, snippet_with_applicability, span_lint_and_sugg};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::in_macro;
+use clippy_utils::source::{snippet, snippet_with_applicability};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{
index 553e6b000ebbc055161347f939a3060f206893b4..12a47a6b7036d9a3e1e707b05066f7200f360979 100644 (file)
@@ -1,20 +1,19 @@
 use std::borrow::Cow;
-use std::ops::Range;
-
-use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then};
-use if_chain::if_chain;
-use rustc_ast::ast::{
-    Expr, ExprKind, ImplKind, Item, ItemKind, LitKind, MacCall, StrLit, StrStyle,
-};
-use rustc_ast::token;
+use std::iter;
+use std::ops::{Deref, Range};
+
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::{snippet_opt, snippet_with_applicability};
+use rustc_ast::ast::{Expr, ExprKind, ImplKind, Item, ItemKind, MacCall, Path, StrLit, StrStyle};
+use rustc_ast::token::{self, LitKind};
 use rustc_ast::tokenstream::TokenStream;
 use rustc_errors::Applicability;
 use rustc_lexer::unescape::{self, EscapeError};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_parse::parser;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::symbol::kw;
-use rustc_span::{sym, BytePos, Span};
+use rustc_span::symbol::{kw, Symbol};
+use rustc_span::{sym, BytePos, Span, DUMMY_SP};
 
 declare_clippy_lint! {
     /// **What it does:** This lint warns when you use `println!("")` to
@@ -355,7 +354,120 @@ fn newline_span(fmtstr: &StrLit) -> Span {
     sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi)
 }
 
+/// Stores a list of replacement spans for each argument, but only if all the replacements used an
+/// empty format string.
+#[derive(Default)]
+struct SimpleFormatArgs {
+    unnamed: Vec<Vec<Span>>,
+    named: Vec<(Symbol, Vec<Span>)>,
+}
+impl SimpleFormatArgs {
+    fn get_unnamed(&self) -> impl Iterator<Item = &[Span]> {
+        self.unnamed.iter().map(|x| match x.as_slice() {
+            // Ignore the dummy span added from out of order format arguments.
+            [DUMMY_SP] => &[],
+            x => x,
+        })
+    }
+
+    fn get_named(&self, n: &Path) -> &[Span] {
+        self.named.iter().find(|x| *n == x.0).map_or(&[], |x| x.1.as_slice())
+    }
+
+    fn push(&mut self, arg: rustc_parse_format::Argument<'_>, span: Span) {
+        use rustc_parse_format::{
+            AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied, FormatSpec,
+        };
+
+        const SIMPLE: FormatSpec<'_> = FormatSpec {
+            fill: None,
+            align: AlignUnknown,
+            flags: 0,
+            precision: CountImplied,
+            precision_span: None,
+            width: CountImplied,
+            width_span: None,
+            ty: "",
+            ty_span: None,
+        };
+
+        match arg.position {
+            ArgumentIs(n) | ArgumentImplicitlyIs(n) => {
+                if self.unnamed.len() <= n {
+                    // Use a dummy span to mark all unseen arguments.
+                    self.unnamed.resize_with(n, || vec![DUMMY_SP]);
+                    if arg.format == SIMPLE {
+                        self.unnamed.push(vec![span]);
+                    } else {
+                        self.unnamed.push(Vec::new());
+                    }
+                } else {
+                    let args = &mut self.unnamed[n];
+                    match (args.as_mut_slice(), arg.format == SIMPLE) {
+                        // A non-empty format string has been seen already.
+                        ([], _) => (),
+                        // Replace the dummy span, if it exists.
+                        ([dummy @ DUMMY_SP], true) => *dummy = span,
+                        ([_, ..], true) => args.push(span),
+                        ([_, ..], false) => *args = Vec::new(),
+                    }
+                }
+            },
+            ArgumentNamed(n) => {
+                if let Some(x) = self.named.iter_mut().find(|x| x.0 == n) {
+                    match x.1.as_slice() {
+                        // A non-empty format string has been seen already.
+                        [] => (),
+                        [_, ..] if arg.format == SIMPLE => x.1.push(span),
+                        [_, ..] => x.1 = Vec::new(),
+                    }
+                } else if arg.format == SIMPLE {
+                    self.named.push((n, vec![span]));
+                } else {
+                    self.named.push((n, Vec::new()));
+                }
+            },
+        };
+    }
+}
+
 impl Write {
+    /// Parses a format string into a collection of spans for each argument. This only keeps track
+    /// of empty format arguments. Will also lint usages of debug format strings outside of debug
+    /// impls.
+    fn parse_fmt_string(&self, cx: &EarlyContext<'_>, str_lit: &StrLit) -> Option<SimpleFormatArgs> {
+        use rustc_parse_format::{ParseMode, Parser, Piece};
+
+        let str_sym = str_lit.symbol_unescaped.as_str();
+        let style = match str_lit.style {
+            StrStyle::Cooked => None,
+            StrStyle::Raw(n) => Some(n as usize),
+        };
+
+        let mut parser = Parser::new(&str_sym, style, snippet_opt(cx, str_lit.span), false, ParseMode::Format);
+        let mut args = SimpleFormatArgs::default();
+
+        while let Some(arg) = parser.next() {
+            let arg = match arg {
+                Piece::String(_) => continue,
+                Piece::NextArgument(arg) => arg,
+            };
+            let span = parser
+                .arg_places
+                .last()
+                .map_or(DUMMY_SP, |&x| str_lit.span.from_inner(x));
+
+            if !self.in_debug_impl && arg.format.ty == "?" {
+                // FIXME: modify rustc's fmt string parser to give us the current span
+                span_lint(cx, USE_DEBUG, span, "use of `Debug`-based formatting");
+            }
+
+            args.push(arg, span);
+        }
+
+        parser.errors.is_empty().then(move || args)
+    }
+
     /// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
     /// `Option`s. The first `Option` of the tuple is the macro's format string. It includes
     /// the contents of the string, whether it's a raw string, and the span of the literal in the
@@ -376,124 +488,97 @@ impl Write {
     /// (Some("string to write: {}"), Some(buf))
     /// ```
     #[allow(clippy::too_many_lines)]
-    fn check_tts<'a>(
-        &self,
-        cx: &EarlyContext<'a>,
-        tts: TokenStream,
-        is_write: bool,
-    ) -> (Option<StrLit>, Option<Expr>) {
-        use rustc_parse_format::{
-            AlignUnknown, ArgumentImplicitlyIs, ArgumentIs, ArgumentNamed, CountImplied,
-            FormatSpec, ParseMode, Parser, Piece,
-        };
-
+    fn check_tts<'a>(&self, cx: &EarlyContext<'a>, tts: TokenStream, is_write: bool) -> (Option<StrLit>, Option<Expr>) {
         let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, false, None);
-        let mut expr: Option<Expr> = None;
-        if is_write {
-            expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
-                Ok(p) => Some(p.into_inner()),
-                Err(_) => return (None, None),
-            };
-            // might be `writeln!(foo)`
-            if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() {
-                return (None, expr);
+        let expr = if is_write {
+            match parser
+                .parse_expr()
+                .map(rustc_ast::ptr::P::into_inner)
+                .map_err(|mut e| e.cancel())
+            {
+                // write!(e, ...)
+                Ok(p) if parser.eat(&token::Comma) => Some(p),
+                // write!(e) or error
+                e => return (None, e.ok()),
             }
-        }
+        } else {
+            None
+        };
 
         let fmtstr = match parser.parse_str_lit() {
             Ok(fmtstr) => fmtstr,
             Err(_) => return (None, expr),
         };
-        let tmp = fmtstr.symbol.as_str();
-        let mut args = vec![];
-        let mut fmt_parser = Parser::new(&tmp, None, None, false, ParseMode::Format);
-        while let Some(piece) = fmt_parser.next() {
-            if !fmt_parser.errors.is_empty() {
-                return (None, expr);
-            }
-            if let Piece::NextArgument(arg) = piece {
-                if !self.in_debug_impl && arg.format.ty == "?" {
-                    // FIXME: modify rustc's fmt string parser to give us the current span
-                    span_lint(
-                        cx,
-                        USE_DEBUG,
-                        parser.prev_token.span,
-                        "use of `Debug`-based formatting",
-                    );
-                }
-                args.push(arg);
-            }
-        }
+
+        let args = match self.parse_fmt_string(cx, &fmtstr) {
+            Some(args) => args,
+            None => return (Some(fmtstr), expr),
+        };
+
         let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
-        let mut idx = 0;
+        let mut unnamed_args = args.get_unnamed();
         loop {
-            const SIMPLE: FormatSpec<'_> = FormatSpec {
-                fill: None,
-                align: AlignUnknown,
-                flags: 0,
-                precision: CountImplied,
-                precision_span: None,
-                width: CountImplied,
-                width_span: None,
-                ty: "",
-                ty_span: None,
-            };
             if !parser.eat(&token::Comma) {
                 return (Some(fmtstr), expr);
             }
+
+            let comma_span = parser.prev_token.span;
             let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|mut err| err.cancel()) {
                 expr
             } else {
                 return (Some(fmtstr), None);
             };
-            match &token_expr.kind {
-                ExprKind::Lit(lit)
-                    if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..)) =>
-                {
-                    let mut all_simple = true;
-                    let mut seen = false;
-                    for arg in &args {
-                        match arg.position {
-                            ArgumentImplicitlyIs(n) | ArgumentIs(n) => {
-                                if n == idx {
-                                    all_simple &= arg.format == SIMPLE;
-                                    seen = true;
-                                }
-                            }
-                            ArgumentNamed(_) => {}
-                        }
-                    }
-                    if all_simple && seen {
-                        span_lint(cx, lint, token_expr.span, "literal with an empty format string");
-                    }
-                    idx += 1;
-                }
-                ExprKind::Assign(lhs, rhs, _) => {
-                    if_chain! {
-                        if let ExprKind::Lit(ref lit) = rhs.kind;
-                        if !matches!(lit.kind, LitKind::Int(..) | LitKind::Float(..));
-                        if let ExprKind::Path(_, p) = &lhs.kind;
-                        then {
-                            let mut all_simple = true;
-                            let mut seen = false;
-                            for arg in &args {
-                                match arg.position {
-                                    ArgumentImplicitlyIs(_) | ArgumentIs(_) => {},
-                                    ArgumentNamed(name) => {
-                                        if *p == name {
-                                            seen = true;
-                                            all_simple &= arg.format == SIMPLE;
-                                        }
-                                    },
-                                }
-                            }
-                            if all_simple && seen {
-                                span_lint(cx, lint, rhs.span, "literal with an empty format string");
-                            }
-                        }
-                    }
+            let (fmt_spans, lit) = match &token_expr.kind {
+                ExprKind::Lit(lit) => (unnamed_args.next().unwrap_or(&[]), lit),
+                ExprKind::Assign(lhs, rhs, _) => match (&lhs.kind, &rhs.kind) {
+                    (ExprKind::Path(_, p), ExprKind::Lit(lit)) => (args.get_named(p), lit),
+                    _ => continue,
+                },
+                _ => {
+                    unnamed_args.next();
+                    continue;
+                },
+            };
+
+            let replacement: String = match lit.token.kind {
+                LitKind::Integer | LitKind::Float | LitKind::Err => continue,
+                LitKind::StrRaw(_) | LitKind::ByteStrRaw(_) if matches!(fmtstr.style, StrStyle::Raw(_)) => {
+                    lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}")
+                },
+                LitKind::Str | LitKind::ByteStr if matches!(fmtstr.style, StrStyle::Cooked) => {
+                    lit.token.symbol.as_str().replace("{", "{{").replace("}", "}}")
+                },
+                LitKind::StrRaw(_) | LitKind::Str | LitKind::ByteStrRaw(_) | LitKind::ByteStr => continue,
+                LitKind::Byte | LitKind::Char => match &*lit.token.symbol.as_str() {
+                    "\"" if matches!(fmtstr.style, StrStyle::Cooked) => "\\\"",
+                    "\"" if matches!(fmtstr.style, StrStyle::Raw(0)) => continue,
+                    "\\\\" if matches!(fmtstr.style, StrStyle::Raw(_)) => "\\",
+                    "\\'" => "'",
+                    "{" => "{{",
+                    "}" => "}}",
+                    x if matches!(fmtstr.style, StrStyle::Raw(_)) && x.starts_with('\\') => continue,
+                    x => x,
                 }
-                _ => idx += 1,
+                .into(),
+                LitKind::Bool => lit.token.symbol.as_str().deref().into(),
+            };
+
+            if !fmt_spans.is_empty() {
+                span_lint_and_then(
+                    cx,
+                    lint,
+                    token_expr.span,
+                    "literal with an empty format string",
+                    |diag| {
+                        diag.multipart_suggestion(
+                            "try this",
+                            iter::once((comma_span.to(token_expr.span), String::new()))
+                                .chain(fmt_spans.iter().cloned().zip(iter::repeat(replacement)))
+                                .collect(),
+                            Applicability::MachineApplicable,
+                        );
+                    },
+                );
             }
         }
     }
@@ -524,17 +609,11 @@ fn lint_print_with_newline(&self, cx: &EarlyContext<'_>, mac: &MacCall) {
                     cx,
                     PRINT_WITH_NEWLINE,
                     mac.span(),
-                    &format!(
-                        "using `{}!()` with a format string that ends in a single newline",
-                        name
-                    ),
+                    &format!("using `{}!()` with a format string that ends in a single newline", name),
                     |err| {
                         err.multipart_suggestion(
                             &format!("use `{}!` instead", suggested),
-                            vec![
-                                (mac.path.span, suggested),
-                                (newline_span(&fmt_str), String::new()),
-                            ],
+                            vec![(mac.path.span, suggested), (newline_span(&fmt_str), String::new())],
                             Applicability::MachineApplicable,
                         );
                     },
index 11d96e15ff1512914717e6d8af61c62f42e7ff64..3b4890ad56063c032f541aecac1768e26fdf553e 100644 (file)
@@ -1,5 +1,5 @@
 use crate::consts::{constant_simple, Constant};
-use crate::utils::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_help;
 use if_chain::if_chain;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
index adf7077e650fd301e2500b1f21bf1ad92c7583ac..2abd033e2a07ed6c55e24b5f1250100c545fc36d 100644 (file)
@@ -1,3 +1,6 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::paths;
+use clippy_utils::ty::{is_normalizable, is_type_diagnostic_item, match_type};
 use if_chain::if_chain;
 use rustc_hir::{self as hir, HirId, ItemKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
@@ -7,8 +10,6 @@
 use rustc_target::abi::LayoutOf as _;
 use rustc_typeck::hir_ty_to_ty;
 
-use crate::utils::{is_normalizable, is_type_diagnostic_item, match_type, paths, span_lint_and_help};
-
 declare_clippy_lint! {
     /// **What it does:** Checks for maps with zero-sized value types anywhere in the code.
     ///
index 9e07f140cf1bedd2fa896981ea4b7776856ec01f..d04c5f889dda0f24a6f9ff408cc02002b1a098a9 100644 (file)
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_utils"
-version = "0.1.52"
+version = "0.1.53"
 authors = ["The Rust Clippy Developers"]
 edition = "2018"
 publish = false
@@ -10,8 +10,6 @@ if_chain = "1.0.0"
 itertools = "0.9"
 regex-syntax = "0.6"
 serde = { version = "1.0", features = ["derive"] }
-smallvec = { version = "1", features = ["union"] }
-toml = "0.5.3"
 unicode-normalization = "0.1"
 rustc-semver="1.1.0"
 
index e202b5061a678dfc8cd418e8023ed25c620de753..eaea3e636f9c39bcc25b5e5260077d74e589b1fd 100644 (file)
@@ -169,9 +169,9 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
         (Path(lq, lp), Path(rq, rp)) => both(lq, rq, |l, r| eq_qself(l, r)) && eq_path(lp, rp),
         (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
         (Struct(lse), Struct(rse)) => {
-            eq_path(&lse.path, &rse.path) &&
-            eq_struct_rest(&lse.rest, &rse.rest) &&
-            unordered_over(&lse.fields, &rse.fields, |l, r| eq_field(l, r))
+            eq_path(&lse.path, &rse.path)
+                && eq_struct_rest(&lse.rest, &rse.rest)
+                && unordered_over(&lse.fields, &rse.fields, |l, r| eq_field(l, r))
         },
         _ => false,
     }
@@ -409,7 +409,7 @@ pub fn eq_use_tree(l: &UseTree, r: &UseTree) -> bool {
 }
 
 pub fn eq_anon_const(l: &AnonConst, r: &AnonConst) -> bool {
-  eq_expr(&l.value, &r.value)
+    eq_expr(&l.value, &r.value)
 }
 
 pub fn eq_use_tree_kind(l: &UseTreeKind, r: &UseTreeKind) -> bool {
index 269be217c2d875bf3db450a39940705666154459..e9c9cb12b9d2e49c40fb15322780732c4b09bb33 100644 (file)
@@ -8,14 +8,16 @@
 
 fn docs_link(diag: &mut DiagnosticBuilder<'_>, lint: &'static Lint) {
     if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() {
-        diag.help(&format!(
-            "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}",
-            &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
-                // extract just major + minor version and ignore patch versions
-                format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap())
-            }),
-            lint.name_lower().replacen("clippy::", "", 1)
-        ));
+        if let Some(lint) = lint.name_lower().strip_prefix("clippy::") {
+            diag.help(&format!(
+                "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}",
+                &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
+                    // extract just major + minor version and ignore patch versions
+                    format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap())
+                }),
+                lint
+            ));
+        }
     }
 }
 
index 8013c4e4fcbe417b369ea1de6be89036ad69e620..88b115a63d78829680ad81a6ea911f1d9228c9fc 100644 (file)
@@ -9,7 +9,8 @@
 //!  - or-fun-call
 //!  - option-if-let-else
 
-use crate::{is_ctor_or_promotable_const_function, is_type_diagnostic_item};
+use crate::is_ctor_or_promotable_const_function;
+use crate::ty::is_type_diagnostic_item;
 use rustc_hir::def::{DefKind, Res};
 
 use rustc_hir::intravisit;
index 7f7d9c5f56a1e0463071f9a9784083beb9c46241..618d33545a4e3fe6c92f61070cb59a0c711468fc 100644 (file)
@@ -1,12 +1,13 @@
 use crate::consts::{constant_context, constant_simple};
-use crate::{differing_macro_contexts, snippet_opt};
+use crate::differing_macro_contexts;
+use crate::source::snippet_opt;
 use rustc_ast::ast::InlineAsmTemplatePiece;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
 use rustc_hir::def::Res;
 use rustc_hir::{
-    BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprKind, ExprField, PatField, FnRetTy,
-    GenericArg, GenericArgs, Guard, HirId, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatKind, Path,
+    BinOpKind, Block, BlockCheckMode, BodyId, BorrowKind, CaptureBy, Expr, ExprField, ExprKind, FnRetTy, GenericArg,
+    GenericArgs, Guard, HirId, InlineAsmOperand, Lifetime, LifetimeName, ParamName, Pat, PatField, PatKind, Path,
     PathSegment, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
 };
 use rustc_lexer::{tokenize, TokenKind};
@@ -79,10 +80,6 @@ pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_
     pub fn eq_path_segments(&mut self, left: &[PathSegment<'_>], right: &[PathSegment<'_>]) -> bool {
         self.inter_expr().eq_path_segments(left, right)
     }
-
-    pub fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool {
-        self.inter_expr().eq_ty_kind(left, right)
-    }
 }
 
 struct HirEqInterExpr<'a, 'b, 'tcx> {
@@ -251,7 +248,7 @@ fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
             (&ExprKind::Struct(ref l_path, ref lf, ref lo), &ExprKind::Struct(ref r_path, ref rf, ref ro)) => {
                 self.eq_qpath(l_path, r_path)
                     && both(lo, ro, |l, r| self.eq_expr(l, r))
-                    && over(lf, rf, |l, r| self.eq_field(l, r))
+                    && over(lf, rf, |l, r| self.eq_expr_field(l, r))
             },
             (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
             (&ExprKind::Unary(l_op, ref le), &ExprKind::Unary(r_op, ref re)) => l_op == r_op && self.eq_expr(le, re),
@@ -266,7 +263,7 @@ fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
         over(left, right, |l, r| self.eq_expr(l, r))
     }
 
-    fn eq_field(&mut self, left: &ExprField<'_>, right: &ExprField<'_>) -> bool {
+    fn eq_expr_field(&mut self, left: &ExprField<'_>, right: &ExprField<'_>) -> bool {
         left.ident.name == right.ident.name && self.eq_expr(&left.expr, &right.expr)
     }
 
@@ -290,7 +287,7 @@ fn eq_lifetime(left: &Lifetime, right: &Lifetime) -> bool {
         left.name == right.name
     }
 
-    fn eq_fieldpat(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool {
+    fn eq_pat_field(&mut self, left: &PatField<'_>, right: &PatField<'_>) -> bool {
         let (PatField { ident: li, pat: lp, .. }, PatField { ident: ri, pat: rp, .. }) = (&left, &right);
         li.name == ri.name && self.eq_pat(lp, rp)
     }
@@ -300,7 +297,7 @@ fn eq_pat(&mut self, left: &Pat<'_>, right: &Pat<'_>) -> bool {
         match (&left.kind, &right.kind) {
             (&PatKind::Box(ref l), &PatKind::Box(ref r)) => self.eq_pat(l, r),
             (&PatKind::Struct(ref lp, ref la, ..), &PatKind::Struct(ref rp, ref ra, ..)) => {
-                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_fieldpat(l, r))
+                self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat_field(l, r))
             },
             (&PatKind::TupleStruct(ref lp, ref la, ls), &PatKind::TupleStruct(ref rp, ref ra, rs)) => {
                 self.eq_qpath(lp, rp) && over(la, ra, |l, r| self.eq_pat(l, r)) && ls == rs
@@ -377,13 +374,9 @@ pub fn eq_path_segment(&mut self, left: &PathSegment<'_>, right: &PathSegment<'_
         left.ident.name == right.ident.name && both(&left.args, &right.args, |l, r| self.eq_path_parameters(l, r))
     }
 
-    fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
-        self.eq_ty_kind(&left.kind, &right.kind)
-    }
-
     #[allow(clippy::similar_names)]
-    fn eq_ty_kind(&mut self, left: &TyKind<'_>, right: &TyKind<'_>) -> bool {
-        match (left, right) {
+    fn eq_ty(&mut self, left: &Ty<'_>, right: &Ty<'_>) -> bool {
+        match (&left.kind, &right.kind) {
             (&TyKind::Slice(ref l_vec), &TyKind::Slice(ref r_vec)) => self.eq_ty(l_vec, r_vec),
             (&TyKind::Array(ref lt, ref ll_id), &TyKind::Array(ref rt, ref rl_id)) => {
                 let cx = self.inner.cx;
index 7d3584333af6ef1313ee63edbce09f29f570b8dc..b613ae9b9180645b2754ac143c58b7de9fa8fa01 100644 (file)
@@ -32,7 +32,7 @@
 pub mod camel_case;
 pub mod comparisons;
 pub mod consts;
-mod diagnostics;
+pub mod diagnostics;
 pub mod eager_or_lazy;
 pub mod higher;
 mod hir_utils;
 pub mod paths;
 pub mod ptr;
 pub mod qualify_min_const_fn;
+pub mod source;
 pub mod sugg;
+pub mod ty;
 pub mod usage;
 pub mod visitors;
 
 pub use self::attrs::*;
-pub use self::diagnostics::*;
 pub use self::hir_utils::{both, eq_expr_value, over, SpanlessEq, SpanlessHash};
 
-use std::borrow::Cow;
 use std::collections::hash_map::Entry;
 use std::hash::BuildHasherDefault;
 
 use if_chain::if_chain;
-use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind, Mutability};
+use rustc_ast::ast::{self, Attribute, BorrowKind, LitKind};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor};
-use rustc_hir::Node;
 use rustc_hir::{
-    def, Arm, Block, Body, Constness, Expr, ExprKind, FnDecl, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, Item,
-    ItemKind, LangItem, MatchSource, Param, Pat, PatKind, Path, PathSegment, QPath, TraitItem, TraitItemKind, TraitRef,
-    TyKind, Unsafety,
+    def, Arm, BindingAnnotation, Block, Body, Constness, CrateItem, Expr, ExprKind, FieldDef, FnDecl, ForeignItem,
+    GenericArgs, GenericParam, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Lifetime, Local,
+    MacroDef, MatchSource, Node, Param, Pat, PatKind, Path, PathSegment, QPath, Stmt, TraitItem, TraitItemKind,
+    TraitRef, TyKind, Variant, Visibility,
 };
-use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::exports::Export;
 use rustc_middle::hir::map::Map;
-use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
-use rustc_middle::ty::{self, layout::IntegerExt, DefIdTree, IntTy, Ty, TyCtxt, TypeFoldable, UintTy};
+use rustc_middle::ty as rustc_ty;
+use rustc_middle::ty::{layout::IntegerExt, DefIdTree, Ty, TyCtxt, TypeFoldable};
 use rustc_semver::RustcVersion;
 use rustc_session::Session;
-use rustc_span::hygiene::{self, ExpnKind, MacroKind};
+use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::source_map::original_sp;
 use rustc_span::sym;
-use rustc_span::symbol::{kw, Symbol};
-use rustc_span::{BytePos, Pos, Span, SyntaxContext, DUMMY_SP};
+use rustc_span::symbol::{kw, Ident, Symbol};
+use rustc_span::{Span, DUMMY_SP};
 use rustc_target::abi::Integer;
-use rustc_trait_selection::traits::query::normalize::AtExt;
-use smallvec::SmallVec;
 
 use crate::consts::{constant, Constant};
-use std::collections::HashMap;
+use crate::ty::is_recursively_primitive_type;
 
 pub fn parse_msrv(msrv: &str, sess: Option<&Session>, span: Option<Span>) -> Option<RustcVersion> {
     if let Ok(version) = RustcVersion::parse(msrv) {
@@ -137,6 +133,58 @@ pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
     rhs.ctxt() != lhs.ctxt()
 }
 
+/// If the given expression is a local binding, find the initializer expression.
+/// If that initializer expression is another local binding, find its initializer again.
+/// This process repeats as long as possible (but usually no more than once). Initializer
+/// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
+/// instead.
+///
+/// Examples:
+/// ```ignore
+/// let abc = 1;
+/// //        ^ output
+/// let def = abc;
+/// dbg!(def)
+/// //   ^^^ input
+///
+/// // or...
+/// let abc = 1;
+/// let def = abc + 2;
+/// //        ^^^^^^^ output
+/// dbg!(def)
+/// //   ^^^ input
+/// ```
+pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr<'b>) -> &'a Expr<'b> {
+    while let Some(init) = path_to_local(expr)
+        .and_then(|id| find_binding_init(cx, id))
+        .filter(|init| cx.typeck_results().expr_adjustments(init).is_empty())
+    {
+        expr = init;
+    }
+    expr
+}
+
+/// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
+/// By only considering immutable bindings, we guarantee that the returned expression represents the
+/// value of the binding wherever it is referenced.
+///
+/// Example: For `let x = 1`, if the `HirId` of `x` is provided, the `Expr` `1` is returned.
+/// Note: If you have an expression that references a binding `x`, use `path_to_local` to get the
+/// canonical binding `HirId`.
+pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Expr<'tcx>> {
+    let hir = cx.tcx.hir();
+    if_chain! {
+        if let Some(Node::Binding(pat)) = hir.find(hir_id);
+        if matches!(pat.kind, PatKind::Binding(BindingAnnotation::Unannotated, ..));
+        let parent = hir.get_parent_node(hir_id);
+        if let Some(Node::Local(local)) = hir.find(parent);
+        then {
+            return local.init;
+        }
+    }
+    None
+}
+
 /// Returns `true` if the given `NodeId` is inside a constant context
 ///
 /// # Example
@@ -184,54 +232,11 @@ pub fn in_macro(span: Span) -> bool {
     }
 }
 
-// If the snippet is empty, it's an attribute that was inserted during macro
-// expansion and we want to ignore those, because they could come from external
-// sources that the user has no control over.
-// For some reason these attributes don't have any expansion info on them, so
-// we have to check it this way until there is a better way.
-pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
-    if let Some(snippet) = snippet_opt(cx, span) {
-        if snippet.is_empty() {
-            return false;
-        }
-    }
-    true
-}
-
 /// Checks if given pattern is a wildcard (`_`)
 pub fn is_wild<'tcx>(pat: &impl std::ops::Deref<Target = Pat<'tcx>>) -> bool {
     matches!(pat.kind, PatKind::Wild)
 }
 
-/// Checks if type is struct, enum or union type with the given def path.
-///
-/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
-/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
-pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
-    match ty.kind() {
-        ty::Adt(adt, _) => match_def_path(cx, adt.did, path),
-        _ => false,
-    }
-}
-
-/// Checks if the type is equal to a diagnostic item
-///
-/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
-pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
-    match ty.kind() {
-        ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
-        _ => false,
-    }
-}
-
-/// Checks if the type is equal to a lang item
-pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
-    match ty.kind() {
-        ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).unwrap() == adt.did,
-        _ => false,
-    }
-}
-
 /// Checks if the first type parameter is a lang item.
 pub fn is_ty_param_lang_item(cx: &LateContext<'_>, qpath: &QPath<'tcx>, item: LangItem) -> Option<&'tcx hir::Ty<'tcx>> {
     let ty = get_qpath_generic_tys(qpath).next()?;
@@ -266,34 +271,37 @@ pub fn is_ty_param_diagnostic_item(
     }
 }
 
-/// Return `true` if the passed `typ` is `isize` or `usize`.
-pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
-    matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
-}
-
 /// Checks if the method call given in `expr` belongs to the given trait.
+/// This is a deprecated function, consider using [`is_trait_method`].
 pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
     let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
     let trt_id = cx.tcx.trait_of_item(def_id);
     trt_id.map_or(false, |trt_id| match_def_path(cx, trt_id, path))
 }
 
-/// Checks if the method call given in `expr` belongs to a trait or other container with a given
+/// Checks if the method call given in `def_id` belongs to a trait or other container with a given
 /// diagnostic item
 pub fn is_diagnostic_assoc_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol) -> bool {
     cx.tcx
         .opt_associated_item(def_id)
         .and_then(|associated_item| match associated_item.container {
-            ty::TraitContainer(assoc_def_id) => Some(assoc_def_id),
-            ty::ImplContainer(assoc_def_id) => match cx.tcx.type_of(assoc_def_id).kind() {
-                ty::Adt(adt, _) => Some(adt.did),
-                ty::Slice(_) => cx.tcx.get_diagnostic_item(sym::slice), // this isn't perfect but it works
+            rustc_ty::TraitContainer(assoc_def_id) => Some(assoc_def_id),
+            rustc_ty::ImplContainer(assoc_def_id) => match cx.tcx.type_of(assoc_def_id).kind() {
+                rustc_ty::Adt(adt, _) => Some(adt.did),
+                rustc_ty::Slice(_) => cx.tcx.get_diagnostic_item(sym::slice), // this isn't perfect but it works
                 _ => None,
             },
         })
         .map_or(false, |assoc_def_id| cx.tcx.is_diagnostic_item(diag_item, assoc_def_id))
 }
 
+/// Checks if the method call given in `expr` belongs to the given trait.
+pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
+    cx.typeck_results()
+        .type_dependent_def_id(expr.hir_id)
+        .map_or(false, |did| is_diagnostic_assoc_item(cx, did, diag_item))
+}
+
 /// Checks if an expression references a variable of the given name.
 pub fn match_var(expr: &Expr<'_>, var: Symbol) -> bool {
     if let ExprKind::Path(QPath::Resolved(None, ref path)) = expr.kind {
@@ -479,26 +487,6 @@ pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
     }
 }
 
-/// Checks whether a type implements a trait.
-/// See also `get_trait_def_id`.
-pub fn implements_trait<'tcx>(
-    cx: &LateContext<'tcx>,
-    ty: Ty<'tcx>,
-    trait_id: DefId,
-    ty_params: &[GenericArg<'tcx>],
-) -> bool {
-    // Do not check on infer_types to avoid panic in evaluate_obligation.
-    if ty.has_infer_types() {
-        return false;
-    }
-    let ty = cx.tcx.erase_regions(ty);
-    if ty.has_escaping_bound_vars() {
-        return false;
-    }
-    let ty_params = cx.tcx.mk_substs(ty_params.iter());
-    cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env))
-}
-
 /// Gets the `hir::TraitRef` of the trait the given method is implemented for.
 ///
 /// Use this if you want to find the `TraitRef` of the `Add` trait in this example:
@@ -526,26 +514,6 @@ pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Optio
     None
 }
 
-/// Checks whether this type implements `Drop`.
-pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-    match ty.ty_adt_def() {
-        Some(def) => def.has_dtor(cx.tcx),
-        None => false,
-    }
-}
-
-/// Checks whether a type can be partially moved.
-pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-    if has_drop(cx, ty) || is_copy(cx, ty) {
-        return false;
-    }
-    match ty.kind() {
-        ty::Param(_) => false,
-        ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))),
-        _ => true,
-    }
-}
-
 /// Returns the method names and argument list of nested method call expressions that make up
 /// `expr`. method/span lists are sorted with the most recent call first.
 pub fn method_calls<'tcx>(
@@ -727,206 +695,6 @@ pub fn find_macro_calls(names: &[&str], body: &Body<'_>) -> Vec<Span> {
     fmc.result
 }
 
-/// Converts a span to a code snippet if available, otherwise use default.
-///
-/// This is useful if you want to provide suggestions for your lint or more generally, if you want
-/// to convert a given `Span` to a `str`.
-///
-/// # Example
-/// ```rust,ignore
-/// snippet(cx, expr.span, "..")
-/// ```
-pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
-    snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
-}
-
-/// Same as `snippet`, but it adapts the applicability level by following rules:
-///
-/// - Applicability level `Unspecified` will never be changed.
-/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
-/// - If the default value is used and the applicability level is `MachineApplicable`, change it to
-/// `HasPlaceholders`
-pub fn snippet_with_applicability<'a, T: LintContext>(
-    cx: &T,
-    span: Span,
-    default: &'a str,
-    applicability: &mut Applicability,
-) -> Cow<'a, str> {
-    if *applicability != Applicability::Unspecified && span.from_expansion() {
-        *applicability = Applicability::MaybeIncorrect;
-    }
-    snippet_opt(cx, span).map_or_else(
-        || {
-            if *applicability == Applicability::MachineApplicable {
-                *applicability = Applicability::HasPlaceholders;
-            }
-            Cow::Borrowed(default)
-        },
-        From::from,
-    )
-}
-
-/// Same as `snippet`, but should only be used when it's clear that the input span is
-/// not a macro argument.
-pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
-    snippet(cx, span.source_callsite(), default)
-}
-
-/// Converts a span to a code snippet. Returns `None` if not available.
-pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
-    cx.sess().source_map().span_to_snippet(span).ok()
-}
-
-/// Converts a span (from a block) to a code snippet if available, otherwise use default.
-///
-/// This trims the code of indentation, except for the first line. Use it for blocks or block-like
-/// things which need to be printed as such.
-///
-/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the
-/// resulting snippet of the given span.
-///
-/// # Example
-///
-/// ```rust,ignore
-/// snippet_block(cx, block.span, "..", None)
-/// // where, `block` is the block of the if expr
-///     if x {
-///         y;
-///     }
-/// // will return the snippet
-/// {
-///     y;
-/// }
-/// ```
-///
-/// ```rust,ignore
-/// snippet_block(cx, block.span, "..", Some(if_expr.span))
-/// // where, `block` is the block of the if expr
-///     if x {
-///         y;
-///     }
-/// // will return the snippet
-/// {
-///         y;
-///     } // aligned with `if`
-/// ```
-/// Note that the first line of the snippet always has 0 indentation.
-pub fn snippet_block<'a, T: LintContext>(
-    cx: &T,
-    span: Span,
-    default: &'a str,
-    indent_relative_to: Option<Span>,
-) -> Cow<'a, str> {
-    let snip = snippet(cx, span, default);
-    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
-    reindent_multiline(snip, true, indent)
-}
-
-/// Same as `snippet_block`, but adapts the applicability level by the rules of
-/// `snippet_with_applicability`.
-pub fn snippet_block_with_applicability<'a, T: LintContext>(
-    cx: &T,
-    span: Span,
-    default: &'a str,
-    indent_relative_to: Option<Span>,
-    applicability: &mut Applicability,
-) -> Cow<'a, str> {
-    let snip = snippet_with_applicability(cx, span, default, applicability);
-    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
-    reindent_multiline(snip, true, indent)
-}
-
-/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
-/// will result in the macro call, rather then the expansion, if the span is from a child context.
-/// If the span is not from a child context, it will be used directly instead.
-///
-/// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node
-/// would result in `box []`. If given the context of the address of expression, this function will
-/// correctly get a snippet of `vec![]`.
-pub fn snippet_with_context(
-    cx: &LateContext<'_>,
-    span: Span,
-    outer: SyntaxContext,
-    default: &'a str,
-    applicability: &mut Applicability,
-) -> Cow<'a, str> {
-    let outer_span = hygiene::walk_chain(span, outer);
-    let span = if outer_span.ctxt() == outer {
-        outer_span
-    } else {
-        // The span is from a macro argument, and the outer context is the macro using the argument
-        if *applicability != Applicability::Unspecified {
-            *applicability = Applicability::MaybeIncorrect;
-        }
-        // TODO: get the argument span.
-        span
-    };
-
-    snippet_with_applicability(cx, span, default, applicability)
-}
-
-/// Returns a new Span that extends the original Span to the first non-whitespace char of the first
-/// line.
-///
-/// ```rust,ignore
-///     let x = ();
-/// //          ^^
-/// // will be converted to
-///     let x = ();
-/// //  ^^^^^^^^^^
-/// ```
-pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
-    first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
-}
-
-fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
-    let line_span = line_span(cx, span);
-    snippet_opt(cx, line_span).and_then(|snip| {
-        snip.find(|c: char| !c.is_whitespace())
-            .map(|pos| line_span.lo() + BytePos::from_usize(pos))
-    })
-}
-
-/// Returns the indentation of the line of a span
-///
-/// ```rust,ignore
-/// let x = ();
-/// //      ^^ -- will return 0
-///     let x = ();
-/// //          ^^ -- will return 4
-/// ```
-pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
-    snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
-}
-
-/// Returns the positon just before rarrow
-///
-/// ```rust,ignore
-/// fn into(self) -> () {}
-///              ^
-/// // in case of unformatted code
-/// fn into2(self)-> () {}
-///               ^
-/// fn into3(self)   -> () {}
-///               ^
-/// ```
-pub fn position_before_rarrow(s: &str) -> Option<usize> {
-    s.rfind("->").map(|rpos| {
-        let mut rpos = rpos;
-        let chars: Vec<char> = s.chars().collect();
-        while rpos > 1 {
-            if let Some(c) = chars.get(rpos - 1) {
-                if c.is_whitespace() {
-                    rpos -= 1;
-                    continue;
-                }
-            }
-            break;
-        }
-        rpos
-    })
-}
-
 /// Extends the span to the beginning of the spans line, incl. whitespaces.
 ///
 /// ```rust,ignore
@@ -944,81 +712,52 @@ fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
     Span::new(line_start, span.hi(), span.ctxt())
 }
 
-/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
-/// Also takes an `Option<String>` which can be put inside the braces.
-pub fn expr_block<'a, T: LintContext>(
-    cx: &T,
-    expr: &Expr<'_>,
-    option: Option<String>,
-    default: &'a str,
-    indent_relative_to: Option<Span>,
-) -> Cow<'a, str> {
-    let code = snippet_block(cx, expr.span, default, indent_relative_to);
-    let string = option.unwrap_or_default();
-    if expr.span.from_expansion() {
-        Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default)))
-    } else if let ExprKind::Block(_, _) = expr.kind {
-        Cow::Owned(format!("{}{}", code, string))
-    } else if string.is_empty() {
-        Cow::Owned(format!("{{ {} }}", code))
-    } else {
-        Cow::Owned(format!("{{\n{};\n{}\n}}", code, string))
+/// Gets the span of the node, if there is one.
+pub fn get_node_span(node: Node<'_>) -> Option<Span> {
+    match node {
+        Node::Param(Param { span, .. })
+        | Node::Item(Item { span, .. })
+        | Node::ForeignItem(ForeignItem { span, .. })
+        | Node::TraitItem(TraitItem { span, .. })
+        | Node::ImplItem(ImplItem { span, .. })
+        | Node::Variant(Variant { span, .. })
+        | Node::Field(FieldDef { span, .. })
+        | Node::Expr(Expr { span, .. })
+        | Node::Stmt(Stmt { span, .. })
+        | Node::PathSegment(PathSegment {
+            ident: Ident { span, .. },
+            ..
+        })
+        | Node::Ty(hir::Ty { span, .. })
+        | Node::TraitRef(TraitRef {
+            path: Path { span, .. },
+            ..
+        })
+        | Node::Binding(Pat { span, .. })
+        | Node::Pat(Pat { span, .. })
+        | Node::Arm(Arm { span, .. })
+        | Node::Block(Block { span, .. })
+        | Node::Local(Local { span, .. })
+        | Node::MacroDef(MacroDef { span, .. })
+        | Node::Lifetime(Lifetime { span, .. })
+        | Node::GenericParam(GenericParam { span, .. })
+        | Node::Visibility(Visibility { span, .. })
+        | Node::Crate(CrateItem { span, .. }) => Some(*span),
+        Node::Ctor(_) | Node::AnonConst(_) => None,
     }
 }
 
-/// Reindent a multiline string with possibility of ignoring the first line.
-#[allow(clippy::needless_pass_by_value)]
-pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
-    let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' ');
-    let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t');
-    reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into()
-}
-
-fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String {
-    let x = s
-        .lines()
-        .skip(ignore_first as usize)
-        .filter_map(|l| {
-            if l.is_empty() {
-                None
-            } else {
-                // ignore empty lines
-                Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0)
-            }
-        })
-        .min()
-        .unwrap_or(0);
-    let indent = indent.unwrap_or(0);
-    s.lines()
-        .enumerate()
-        .map(|(i, l)| {
-            if (ignore_first && i == 0) || l.is_empty() {
-                l.to_owned()
-            } else if x > indent {
-                l.split_at(x - indent).1.to_owned()
-            } else {
-                " ".repeat(indent - x) + l
-            }
-        })
-        .collect::<Vec<String>>()
-        .join("\n")
+/// Gets the parent node, if any.
+pub fn get_parent_node(tcx: TyCtxt<'_>, id: HirId) -> Option<Node<'_>> {
+    tcx.hir().parent_iter(id).next().map(|(_, node)| node)
 }
 
 /// Gets the parent expression, if any â€“- this is useful to constrain a lint.
 pub fn get_parent_expr<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
-    let map = &cx.tcx.hir();
-    let hir_id = e.hir_id;
-    let parent_id = map.get_parent_node(hir_id);
-    if hir_id == parent_id {
-        return None;
+    match get_parent_node(cx.tcx, e.hir_id) {
+        Some(Node::Expr(parent)) => Some(parent),
+        _ => None,
     }
-    map.find(parent_id).and_then(|node| {
-        if let Node::Expr(parent) = node {
-            Some(parent)
-        } else {
-            None
-        }
-    })
 }
 
 pub fn get_enclosing_block<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<&'tcx Block<'tcx>> {
@@ -1058,24 +797,24 @@ pub fn get_parent_as_impl(tcx: TyCtxt<'_>, id: HirId) -> Option<&Impl<'_>> {
     }
 }
 
-/// Returns the base type for HIR references and pointers.
-pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
-    match ty.kind {
-        TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(&mut_ty.ty),
-        _ => ty,
-    }
-}
-
-/// Returns the base type for references and raw pointers, and count reference
-/// depth.
-pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
-    fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
-        match ty.kind() {
-            ty::Ref(_, ty, _) => inner(ty, depth + 1),
-            _ => (ty, depth),
-        }
+/// Checks if the given expression is the else clause in the expression `if let .. {} else {}`
+pub fn is_else_clause_of_if_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
+    let map = tcx.hir();
+    let mut iter = map.parent_iter(expr.hir_id);
+    let arm_id = match iter.next() {
+        Some((id, Node::Arm(..))) => id,
+        _ => return false,
+    };
+    match iter.next() {
+        Some((
+            _,
+            Node::Expr(Expr {
+                kind: ExprKind::Match(_, [_, else_arm], kind),
+                ..
+            }),
+        )) => else_arm.hir_id == arm_id && matches!(kind, MatchSource::IfLetDesugar { .. }),
+        _ => false,
     }
-    inner(ty, 0)
 }
 
 /// Checks whether the given expression is a constant integer of the given value.
@@ -1173,26 +912,6 @@ pub fn return_ty<'tcx>(cx: &LateContext<'tcx>, fn_item: hir::HirId) -> Ty<'tcx>
     cx.tcx.erase_late_bound_regions(ret_ty)
 }
 
-/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
-pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool {
-    ty.walk().any(|inner| match inner.unpack() {
-        GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty),
-        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
-    })
-}
-
-/// Returns `true` if the given type is an `unsafe` function.
-pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-    match ty.kind() {
-        ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
-        _ => false,
-    }
-}
-
-pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-    ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env)
-}
-
 /// Checks if an expression is constructing a tuple-like enum variant or struct
 pub fn is_ctor_or_promotable_const_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     if let ExprKind::Call(ref fun, _) = expr.kind {
@@ -1241,11 +960,13 @@ fn are_refutable<'a, I: Iterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, mut
         },
         PatKind::Slice(ref head, ref middle, ref tail) => {
             match &cx.typeck_results().node_type(pat.hir_id).kind() {
-                ty::Slice(..) => {
+                rustc_ty::Slice(..) => {
                     // [..] is the only irrefutable slice pattern.
                     !head.is_empty() || middle.is_none() || !tail.is_empty()
                 },
-                ty::Array(..) => are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat)),
+                rustc_ty::Array(..) => {
+                    are_refutable(cx, head.iter().chain(middle).chain(tail.iter()).map(|pat| &**pat))
+                },
                 _ => {
                     // unreachable!()
                     true
@@ -1255,6 +976,16 @@ fn are_refutable<'a, I: Iterator<Item = &'a Pat<'a>>>(cx: &LateContext<'_>, mut
     }
 }
 
+/// If the pattern is an `or` pattern, call the function once for each sub pattern. Otherwise, call
+/// the function once on the given pattern.
+pub fn recurse_or_patterns<'tcx, F: FnMut(&'tcx Pat<'tcx>)>(pat: &'tcx Pat<'tcx>, mut f: F) {
+    if let PatKind::Or(pats) = pat.kind {
+        pats.iter().cloned().for_each(f)
+    } else {
+        f(pat)
+    }
+}
+
 /// Checks for the `#[automatically_derived]` attribute all `#[derive]`d
 /// implementations have.
 pub fn is_automatically_derived(attrs: &[ast::Attribute]) -> bool {
@@ -1357,64 +1088,31 @@ pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {
     pat
 }
 
-pub fn int_bits(tcx: TyCtxt<'_>, ity: ty::IntTy) -> u64 {
+pub fn int_bits(tcx: TyCtxt<'_>, ity: rustc_ty::IntTy) -> u64 {
     Integer::from_int_ty(&tcx, ity).size().bits()
 }
 
 #[allow(clippy::cast_possible_wrap)]
 /// Turn a constant int byte representation into an i128
-pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: ty::IntTy) -> i128 {
+pub fn sext(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::IntTy) -> i128 {
     let amt = 128 - int_bits(tcx, ity);
     ((u as i128) << amt) >> amt
 }
 
 #[allow(clippy::cast_sign_loss)]
 /// clip unused bytes
-pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: ty::IntTy) -> u128 {
+pub fn unsext(tcx: TyCtxt<'_>, u: i128, ity: rustc_ty::IntTy) -> u128 {
     let amt = 128 - int_bits(tcx, ity);
     ((u as u128) << amt) >> amt
 }
 
 /// clip unused bytes
-pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: ty::UintTy) -> u128 {
+pub fn clip(tcx: TyCtxt<'_>, u: u128, ity: rustc_ty::UintTy) -> u128 {
     let bits = Integer::from_uint_ty(&tcx, ity).size().bits();
     let amt = 128 - bits;
     (u << amt) >> amt
 }
 
-/// Removes block comments from the given `Vec` of lines.
-///
-/// # Examples
-///
-/// ```rust,ignore
-/// without_block_comments(vec!["/*", "foo", "*/"]);
-/// // => vec![]
-///
-/// without_block_comments(vec!["bar", "/*", "foo", "*/"]);
-/// // => vec!["bar"]
-/// ```
-pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> {
-    let mut without = vec![];
-
-    let mut nest_level = 0;
-
-    for line in lines {
-        if line.contains("/*") {
-            nest_level += 1;
-            continue;
-        } else if line.contains("*/") {
-            nest_level -= 1;
-            continue;
-        }
-
-        if nest_level == 0 {
-            without.push(line);
-        }
-    }
-
-    without
-}
-
 pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
     let map = &tcx.hir();
     let mut prev_enclosing_node = None;
@@ -1429,47 +1127,6 @@ pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool
     false
 }
 
-/// Returns true if ty has `iter` or `iter_mut` methods
-pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
-    // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
-    // exists and has the desired signature. Unfortunately FnCtxt is not exported
-    // so we can't use its `lookup_method` method.
-    let into_iter_collections: &[Symbol] = &[
-        sym::vec_type,
-        sym::option_type,
-        sym::result_type,
-        sym::BTreeMap,
-        sym::BTreeSet,
-        sym::vecdeque_type,
-        sym::LinkedList,
-        sym::BinaryHeap,
-        sym::hashset_type,
-        sym::hashmap_type,
-        sym::PathBuf,
-        sym::Path,
-        sym::Receiver,
-    ];
-
-    let ty_to_check = match probably_ref_ty.kind() {
-        ty::Ref(_, ty_to_check, _) => ty_to_check,
-        _ => probably_ref_ty,
-    };
-
-    let def_id = match ty_to_check.kind() {
-        ty::Array(..) => return Some(sym::array),
-        ty::Slice(..) => return Some(sym::slice),
-        ty::Adt(adt, _) => adt.did,
-        _ => return None,
-    };
-
-    for &name in into_iter_collections {
-        if cx.tcx.is_diagnostic_item(name, def_id) {
-            return Some(cx.tcx.item_name(def_id));
-        }
-    }
-    None
-}
-
 /// Matches a function call with the given path and returns the arguments.
 ///
 /// Usage:
@@ -1494,51 +1151,6 @@ pub fn match_function_call<'tcx>(
     None
 }
 
-// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
-// this function can be removed once the `normalizie` method does not panic when normalization does
-// not succeed
-/// Checks if `Ty` is normalizable. This function is useful
-/// to avoid crashes on `layout_of`.
-pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
-    is_normalizable_helper(cx, param_env, ty, &mut HashMap::new())
-}
-
-fn is_normalizable_helper<'tcx>(
-    cx: &LateContext<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    ty: Ty<'tcx>,
-    cache: &mut HashMap<Ty<'tcx>, bool>,
-) -> bool {
-    if let Some(&cached_result) = cache.get(ty) {
-        return cached_result;
-    }
-    // prevent recursive loops, false-negative is better than endless loop leading to stack overflow
-    cache.insert(ty, false);
-    let result = cx.tcx.infer_ctxt().enter(|infcx| {
-        let cause = rustc_middle::traits::ObligationCause::dummy();
-        if infcx.at(&cause, param_env).normalize(ty).is_ok() {
-            match ty.kind() {
-                ty::Adt(def, substs) => def.variants.iter().all(|variant| {
-                    variant
-                        .fields
-                        .iter()
-                        .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
-                }),
-                _ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
-                    GenericArgKind::Type(inner_ty) if inner_ty != ty => {
-                        is_normalizable_helper(cx, param_env, inner_ty, cache)
-                    },
-                    _ => true, // if inner_ty == ty, we've already checked it
-                }),
-            }
-        } else {
-            false
-        }
-    });
-    cache.insert(ty, result);
-    result
-}
-
 pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
     // We have to convert `syms` to `&[Symbol]` here because rustc's `match_def_path`
     // accepts only that. We should probably move to Symbols in Clippy as well.
@@ -1568,11 +1180,9 @@ pub fn match_panic_def_id(cx: &LateContext<'_>, did: DefId) -> bool {
 /// sequence of `if/else`.
 /// E.g., this returns `([a, b], [c, d, e])` for the expression
 /// `if a { c } else if b { d } else { e }`.
-pub fn if_sequence<'tcx>(
-    mut expr: &'tcx Expr<'tcx>,
-) -> (SmallVec<[&'tcx Expr<'tcx>; 1]>, SmallVec<[&'tcx Block<'tcx>; 1]>) {
-    let mut conds = SmallVec::new();
-    let mut blocks: SmallVec<[&Block<'_>; 1]> = SmallVec::new();
+pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>, Vec<&'tcx Block<'tcx>>) {
+    let mut conds = Vec::new();
+    let mut blocks: Vec<&Block<'_>> = Vec::new();
 
     while let ExprKind::If(ref cond, ref then_expr, ref else_expr) = expr.kind {
         conds.push(&**cond);
@@ -1624,44 +1234,6 @@ pub fn must_use_attr(attrs: &[Attribute]) -> Option<&Attribute> {
     attr_by_name(attrs, "must_use")
 }
 
-// Returns whether the type has #[must_use] attribute
-pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-    match ty.kind() {
-        ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(),
-        ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(),
-        ty::Slice(ref ty)
-        | ty::Array(ref ty, _)
-        | ty::RawPtr(ty::TypeAndMut { ref ty, .. })
-        | ty::Ref(_, ref ty, _) => {
-            // for the Array case we don't need to care for the len == 0 case
-            // because we don't want to lint functions returning empty arrays
-            is_must_use_ty(cx, *ty)
-        },
-        ty::Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)),
-        ty::Opaque(ref def_id, _) => {
-            for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
-                if let ty::PredicateKind::Trait(trait_predicate, _) = predicate.kind().skip_binder() {
-                    if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() {
-                        return true;
-                    }
-                }
-            }
-            false
-        },
-        ty::Dynamic(binder, _) => {
-            for predicate in binder.iter() {
-                if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
-                    if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
-                        return true;
-                    }
-                }
-            }
-            false
-        },
-        _ => false,
-    }
-}
-
 // check if expr is calling method or function with #[must_use] attribute
 pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     let did = match expr.kind {
@@ -1756,18 +1328,6 @@ pub fn run_lints(cx: &LateContext<'_>, lints: &[&'static Lint], id: HirId) -> bo
     })
 }
 
-/// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point
-/// number type, a str, or an array, slice, or tuple of those types).
-pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
-    match ty.kind() {
-        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
-        ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
-        ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
-        ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type),
-        _ => false,
-    }
-}
-
 /// Returns Option<String> where String is a textual representation of the type encapsulated in the
 /// slice iff the given expression is a slice of primitives (as defined in the
 /// `is_recursively_primitive_type` function) and None otherwise.
@@ -1775,9 +1335,9 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<S
     let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
     let expr_kind = expr_type.kind();
     let is_primitive = match expr_kind {
-        ty::Slice(element_type) => is_recursively_primitive_type(element_type),
-        ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &ty::Slice(_)) => {
-            if let ty::Slice(element_type) = inner_ty.kind() {
+        rustc_ty::Slice(element_type) => is_recursively_primitive_type(element_type),
+        rustc_ty::Ref(_, inner_ty, _) if matches!(inner_ty.kind(), &rustc_ty::Slice(_)) => {
+            if let rustc_ty::Slice(element_type) = inner_ty.kind() {
                 is_recursively_primitive_type(element_type)
             } else {
                 unreachable!()
@@ -1790,9 +1350,9 @@ pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<S
         // if we have wrappers like Array, Slice or Tuple, print these
         // and get the type enclosed in the slice ref
         match expr_type.peel_refs().walk().nth(1).unwrap().expect_ty().kind() {
-            ty::Slice(..) => return Some("slice".into()),
-            ty::Array(..) => return Some("array".into()),
-            ty::Tuple(..) => return Some("tuple".into()),
+            rustc_ty::Slice(..) => return Some("slice".into()),
+            rustc_ty::Array(..) => return Some("array".into()),
+            rustc_ty::Tuple(..) => return Some("tuple".into()),
             _ => {
                 // is_recursively_primitive_type() should have taken care
                 // of the rest and we can rely on the type that is found
@@ -1876,32 +1436,6 @@ fn f(expr: &'a Expr<'a>, count: usize) -> (&'a Expr<'a>, usize) {
     f(expr, 0)
 }
 
-/// Peels off all references on the type. Returns the underlying type and the number of references
-/// removed.
-pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
-    fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
-        if let ty::Ref(_, ty, _) = ty.kind() {
-            peel(ty, count + 1)
-        } else {
-            (ty, count)
-        }
-    }
-    peel(ty, 0)
-}
-
-/// Peels off all references on the type.Returns the underlying type, the number of references
-/// removed, and whether the pointer is ultimately mutable or not.
-pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
-    fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
-        match ty.kind() {
-            ty::Ref(_, ty, Mutability::Mut) => f(ty, count + 1, mutability),
-            ty::Ref(_, ty, Mutability::Not) => f(ty, count + 1, Mutability::Not),
-            _ => (ty, count, mutability),
-        }
-    }
-    f(ty, 0, Mutability::Mut)
-}
-
 #[macro_export]
 macro_rules! unwrap_cargo_metadata {
     ($cx: ident, $lint: ident, $deps: expr) => {{
@@ -1955,100 +1489,3 @@ pub fn is_some_ctor(cx: &LateContext<'_>, res: Res) -> bool {
     }
     false
 }
-
-#[cfg(test)]
-mod test {
-    use super::{reindent_multiline, without_block_comments};
-
-    #[test]
-    fn test_reindent_multiline_single_line() {
-        assert_eq!("", reindent_multiline("".into(), false, None));
-        assert_eq!("...", reindent_multiline("...".into(), false, None));
-        assert_eq!("...", reindent_multiline("    ...".into(), false, None));
-        assert_eq!("...", reindent_multiline("\t...".into(), false, None));
-        assert_eq!("...", reindent_multiline("\t\t...".into(), false, None));
-    }
-
-    #[test]
-    #[rustfmt::skip]
-    fn test_reindent_multiline_block() {
-        assert_eq!("\
-    if x {
-        y
-    } else {
-        z
-    }", reindent_multiline("    if x {
-            y
-        } else {
-            z
-        }".into(), false, None));
-        assert_eq!("\
-    if x {
-    \ty
-    } else {
-    \tz
-    }", reindent_multiline("    if x {
-        \ty
-        } else {
-        \tz
-        }".into(), false, None));
-    }
-
-    #[test]
-    #[rustfmt::skip]
-    fn test_reindent_multiline_empty_line() {
-        assert_eq!("\
-    if x {
-        y
-
-    } else {
-        z
-    }", reindent_multiline("    if x {
-            y
-
-        } else {
-            z
-        }".into(), false, None));
-    }
-
-    #[test]
-    #[rustfmt::skip]
-    fn test_reindent_multiline_lines_deeper() {
-        assert_eq!("\
-        if x {
-            y
-        } else {
-            z
-        }", reindent_multiline("\
-    if x {
-        y
-    } else {
-        z
-    }".into(), true, Some(8)));
-    }
-
-    #[test]
-    fn test_without_block_comments_lines_without_block_comments() {
-        let result = without_block_comments(vec!["/*", "", "*/"]);
-        println!("result: {:?}", result);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]);
-        assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]);
-
-        let result = without_block_comments(vec!["/* rust", "", "*/"]);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["/* one-line comment */"]);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]);
-        assert!(result.is_empty());
-
-        let result = without_block_comments(vec!["foo", "bar", "baz"]);
-        assert_eq!(result, vec!["foo", "bar", "baz"]);
-    }
-}
index 560614efc749e1b1fd29102de9a7322d6284d31b..11a446e42a4e185c846f4e42bf145b3a9f5a27c1 100644 (file)
@@ -61,7 +61,6 @@
 pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
 pub const IPADDR_V4: [&str; 4] = ["std", "net", "IpAddr", "V4"];
 pub const IPADDR_V6: [&str; 4] = ["std", "net", "IpAddr", "V6"];
-pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
 #[cfg(feature = "internal-lints")]
 pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
 #[cfg(feature = "internal-lints")]
index df6143edbcaf09a98ef6eb3bfcb88a8eb6123681..5885cc83560ff10d62fdec714d415acee760bfb9 100644 (file)
@@ -1,4 +1,5 @@
-use crate::{get_pat_name, match_var, snippet};
+use crate::source::snippet;
+use crate::{get_pat_name, match_var};
 use rustc_hir::intravisit::{walk_expr, NestedVisitorMap, Visitor};
 use rustc_hir::{Body, BodyId, Expr, ExprKind, Param};
 use rustc_lint::LateContext;
diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs
new file mode 100644 (file)
index 0000000..2d794d4
--- /dev/null
@@ -0,0 +1,420 @@
+//! Utils for extracting, inspecting or transforming source code
+
+#![allow(clippy::module_name_repetitions)]
+
+use crate::line_span;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LintContext};
+use rustc_span::hygiene;
+use rustc_span::{BytePos, Pos, Span, SyntaxContext};
+use std::borrow::Cow;
+
+/// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
+/// Also takes an `Option<String>` which can be put inside the braces.
+pub fn expr_block<'a, T: LintContext>(
+    cx: &T,
+    expr: &Expr<'_>,
+    option: Option<String>,
+    default: &'a str,
+    indent_relative_to: Option<Span>,
+) -> Cow<'a, str> {
+    let code = snippet_block(cx, expr.span, default, indent_relative_to);
+    let string = option.unwrap_or_default();
+    if expr.span.from_expansion() {
+        Cow::Owned(format!("{{ {} }}", snippet_with_macro_callsite(cx, expr.span, default)))
+    } else if let ExprKind::Block(_, _) = expr.kind {
+        Cow::Owned(format!("{}{}", code, string))
+    } else if string.is_empty() {
+        Cow::Owned(format!("{{ {} }}", code))
+    } else {
+        Cow::Owned(format!("{{\n{};\n{}\n}}", code, string))
+    }
+}
+
+/// Returns a new Span that extends the original Span to the first non-whitespace char of the first
+/// line.
+///
+/// ```rust,ignore
+///     let x = ();
+/// //          ^^
+/// // will be converted to
+///     let x = ();
+/// //  ^^^^^^^^^^
+/// ```
+pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
+    first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
+}
+
+fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
+    let line_span = line_span(cx, span);
+    snippet_opt(cx, line_span).and_then(|snip| {
+        snip.find(|c: char| !c.is_whitespace())
+            .map(|pos| line_span.lo() + BytePos::from_usize(pos))
+    })
+}
+
+/// Returns the indentation of the line of a span
+///
+/// ```rust,ignore
+/// let x = ();
+/// //      ^^ -- will return 0
+///     let x = ();
+/// //          ^^ -- will return 4
+/// ```
+pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
+    snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
+}
+
+// If the snippet is empty, it's an attribute that was inserted during macro
+// expansion and we want to ignore those, because they could come from external
+// sources that the user has no control over.
+// For some reason these attributes don't have any expansion info on them, so
+// we have to check it this way until there is a better way.
+pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
+    if let Some(snippet) = snippet_opt(cx, span) {
+        if snippet.is_empty() {
+            return false;
+        }
+    }
+    true
+}
+
+/// Returns the positon just before rarrow
+///
+/// ```rust,ignore
+/// fn into(self) -> () {}
+///              ^
+/// // in case of unformatted code
+/// fn into2(self)-> () {}
+///               ^
+/// fn into3(self)   -> () {}
+///               ^
+/// ```
+pub fn position_before_rarrow(s: &str) -> Option<usize> {
+    s.rfind("->").map(|rpos| {
+        let mut rpos = rpos;
+        let chars: Vec<char> = s.chars().collect();
+        while rpos > 1 {
+            if let Some(c) = chars.get(rpos - 1) {
+                if c.is_whitespace() {
+                    rpos -= 1;
+                    continue;
+                }
+            }
+            break;
+        }
+        rpos
+    })
+}
+
+/// Reindent a multiline string with possibility of ignoring the first line.
+#[allow(clippy::needless_pass_by_value)]
+pub fn reindent_multiline(s: Cow<'_, str>, ignore_first: bool, indent: Option<usize>) -> Cow<'_, str> {
+    let s_space = reindent_multiline_inner(&s, ignore_first, indent, ' ');
+    let s_tab = reindent_multiline_inner(&s_space, ignore_first, indent, '\t');
+    reindent_multiline_inner(&s_tab, ignore_first, indent, ' ').into()
+}
+
+fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>, ch: char) -> String {
+    let x = s
+        .lines()
+        .skip(ignore_first as usize)
+        .filter_map(|l| {
+            if l.is_empty() {
+                None
+            } else {
+                // ignore empty lines
+                Some(l.char_indices().find(|&(_, x)| x != ch).unwrap_or((l.len(), ch)).0)
+            }
+        })
+        .min()
+        .unwrap_or(0);
+    let indent = indent.unwrap_or(0);
+    s.lines()
+        .enumerate()
+        .map(|(i, l)| {
+            if (ignore_first && i == 0) || l.is_empty() {
+                l.to_owned()
+            } else if x > indent {
+                l.split_at(x - indent).1.to_owned()
+            } else {
+                " ".repeat(indent - x) + l
+            }
+        })
+        .collect::<Vec<String>>()
+        .join("\n")
+}
+
+/// Converts a span to a code snippet if available, otherwise use default.
+///
+/// This is useful if you want to provide suggestions for your lint or more generally, if you want
+/// to convert a given `Span` to a `str`.
+///
+/// # Example
+/// ```rust,ignore
+/// snippet(cx, expr.span, "..")
+/// ```
+pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
+    snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
+}
+
+/// Same as `snippet`, but it adapts the applicability level by following rules:
+///
+/// - Applicability level `Unspecified` will never be changed.
+/// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
+/// - If the default value is used and the applicability level is `MachineApplicable`, change it to
+/// `HasPlaceholders`
+pub fn snippet_with_applicability<'a, T: LintContext>(
+    cx: &T,
+    span: Span,
+    default: &'a str,
+    applicability: &mut Applicability,
+) -> Cow<'a, str> {
+    if *applicability != Applicability::Unspecified && span.from_expansion() {
+        *applicability = Applicability::MaybeIncorrect;
+    }
+    snippet_opt(cx, span).map_or_else(
+        || {
+            if *applicability == Applicability::MachineApplicable {
+                *applicability = Applicability::HasPlaceholders;
+            }
+            Cow::Borrowed(default)
+        },
+        From::from,
+    )
+}
+
+/// Same as `snippet`, but should only be used when it's clear that the input span is
+/// not a macro argument.
+pub fn snippet_with_macro_callsite<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
+    snippet(cx, span.source_callsite(), default)
+}
+
+/// Converts a span to a code snippet. Returns `None` if not available.
+pub fn snippet_opt<T: LintContext>(cx: &T, span: Span) -> Option<String> {
+    cx.sess().source_map().span_to_snippet(span).ok()
+}
+
+/// Converts a span (from a block) to a code snippet if available, otherwise use default.
+///
+/// This trims the code of indentation, except for the first line. Use it for blocks or block-like
+/// things which need to be printed as such.
+///
+/// The `indent_relative_to` arg can be used, to provide a span, where the indentation of the
+/// resulting snippet of the given span.
+///
+/// # Example
+///
+/// ```rust,ignore
+/// snippet_block(cx, block.span, "..", None)
+/// // where, `block` is the block of the if expr
+///     if x {
+///         y;
+///     }
+/// // will return the snippet
+/// {
+///     y;
+/// }
+/// ```
+///
+/// ```rust,ignore
+/// snippet_block(cx, block.span, "..", Some(if_expr.span))
+/// // where, `block` is the block of the if expr
+///     if x {
+///         y;
+///     }
+/// // will return the snippet
+/// {
+///         y;
+///     } // aligned with `if`
+/// ```
+/// Note that the first line of the snippet always has 0 indentation.
+pub fn snippet_block<'a, T: LintContext>(
+    cx: &T,
+    span: Span,
+    default: &'a str,
+    indent_relative_to: Option<Span>,
+) -> Cow<'a, str> {
+    let snip = snippet(cx, span, default);
+    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
+    reindent_multiline(snip, true, indent)
+}
+
+/// Same as `snippet_block`, but adapts the applicability level by the rules of
+/// `snippet_with_applicability`.
+pub fn snippet_block_with_applicability<'a, T: LintContext>(
+    cx: &T,
+    span: Span,
+    default: &'a str,
+    indent_relative_to: Option<Span>,
+    applicability: &mut Applicability,
+) -> Cow<'a, str> {
+    let snip = snippet_with_applicability(cx, span, default, applicability);
+    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
+    reindent_multiline(snip, true, indent)
+}
+
+/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
+/// will result in the macro call, rather then the expansion, if the span is from a child context.
+/// If the span is not from a child context, it will be used directly instead.
+///
+/// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node
+/// would result in `box []`. If given the context of the address of expression, this function will
+/// correctly get a snippet of `vec![]`.
+///
+/// This will also return whether or not the snippet is a macro call.
+pub fn snippet_with_context(
+    cx: &LateContext<'_>,
+    span: Span,
+    outer: SyntaxContext,
+    default: &'a str,
+    applicability: &mut Applicability,
+) -> (Cow<'a, str>, bool) {
+    let outer_span = hygiene::walk_chain(span, outer);
+    let (span, is_macro_call) = if outer_span.ctxt() == outer {
+        (outer_span, span.ctxt() != outer)
+    } else {
+        // The span is from a macro argument, and the outer context is the macro using the argument
+        if *applicability != Applicability::Unspecified {
+            *applicability = Applicability::MaybeIncorrect;
+        }
+        // TODO: get the argument span.
+        (span, false)
+    };
+
+    (
+        snippet_with_applicability(cx, span, default, applicability),
+        is_macro_call,
+    )
+}
+
+/// Removes block comments from the given `Vec` of lines.
+///
+/// # Examples
+///
+/// ```rust,ignore
+/// without_block_comments(vec!["/*", "foo", "*/"]);
+/// // => vec![]
+///
+/// without_block_comments(vec!["bar", "/*", "foo", "*/"]);
+/// // => vec!["bar"]
+/// ```
+pub fn without_block_comments(lines: Vec<&str>) -> Vec<&str> {
+    let mut without = vec![];
+
+    let mut nest_level = 0;
+
+    for line in lines {
+        if line.contains("/*") {
+            nest_level += 1;
+            continue;
+        } else if line.contains("*/") {
+            nest_level -= 1;
+            continue;
+        }
+
+        if nest_level == 0 {
+            without.push(line);
+        }
+    }
+
+    without
+}
+
+#[cfg(test)]
+mod test {
+    use super::{reindent_multiline, without_block_comments};
+
+    #[test]
+    fn test_reindent_multiline_single_line() {
+        assert_eq!("", reindent_multiline("".into(), false, None));
+        assert_eq!("...", reindent_multiline("...".into(), false, None));
+        assert_eq!("...", reindent_multiline("    ...".into(), false, None));
+        assert_eq!("...", reindent_multiline("\t...".into(), false, None));
+        assert_eq!("...", reindent_multiline("\t\t...".into(), false, None));
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn test_reindent_multiline_block() {
+        assert_eq!("\
+    if x {
+        y
+    } else {
+        z
+    }", reindent_multiline("    if x {
+            y
+        } else {
+            z
+        }".into(), false, None));
+        assert_eq!("\
+    if x {
+    \ty
+    } else {
+    \tz
+    }", reindent_multiline("    if x {
+        \ty
+        } else {
+        \tz
+        }".into(), false, None));
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn test_reindent_multiline_empty_line() {
+        assert_eq!("\
+    if x {
+        y
+
+    } else {
+        z
+    }", reindent_multiline("    if x {
+            y
+
+        } else {
+            z
+        }".into(), false, None));
+    }
+
+    #[test]
+    #[rustfmt::skip]
+    fn test_reindent_multiline_lines_deeper() {
+        assert_eq!("\
+        if x {
+            y
+        } else {
+            z
+        }", reindent_multiline("\
+    if x {
+        y
+    } else {
+        z
+    }".into(), true, Some(8)));
+    }
+
+    #[test]
+    fn test_without_block_comments_lines_without_block_comments() {
+        let result = without_block_comments(vec!["/*", "", "*/"]);
+        println!("result: {:?}", result);
+        assert!(result.is_empty());
+
+        let result = without_block_comments(vec!["", "/*", "", "*/", "#[crate_type = \"lib\"]", "/*", "", "*/", ""]);
+        assert_eq!(result, vec!["", "#[crate_type = \"lib\"]", ""]);
+
+        let result = without_block_comments(vec!["/* rust", "", "*/"]);
+        assert!(result.is_empty());
+
+        let result = without_block_comments(vec!["/* one-line comment */"]);
+        assert!(result.is_empty());
+
+        let result = without_block_comments(vec!["/* nested", "/* multi-line", "comment", "*/", "test", "*/"]);
+        assert!(result.is_empty());
+
+        let result = without_block_comments(vec!["/* nested /* inline /* comment */ test */ */"]);
+        assert!(result.is_empty());
+
+        let result = without_block_comments(vec!["foo", "bar", "baz"]);
+        assert_eq!(result, vec!["foo", "bar", "baz"]);
+    }
+}
index d4f6f4281d368ef32bef21978057a9b5a21cdb87..b2fe4317154efb708b6719f9881b2dce0295aaa8 100644 (file)
@@ -1,7 +1,8 @@
 //! Contains utility functions to generate suggestions.
 #![deny(clippy::missing_docs_in_private_items)]
 
-use crate::{higher, snippet, snippet_opt, snippet_with_macro_callsite};
+use crate::higher;
+use crate::source::{snippet, snippet_opt, snippet_with_macro_callsite};
 use rustc_ast::util::parser::AssocOp;
 use rustc_ast::{ast, token};
 use rustc_ast_pretty::pprust::token_kind_to_string;
diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs
new file mode 100644 (file)
index 0000000..807cfbc
--- /dev/null
@@ -0,0 +1,305 @@
+//! Util methods for [`rustc_middle::ty`]
+
+#![allow(clippy::module_name_repetitions)]
+
+use std::collections::HashMap;
+
+use rustc_ast::ast::Mutability;
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_hir::{TyKind, Unsafety};
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_lint::LateContext;
+use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
+use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TypeFoldable, UintTy};
+use rustc_span::sym;
+use rustc_span::symbol::Symbol;
+use rustc_span::DUMMY_SP;
+use rustc_trait_selection::traits::query::normalize::AtExt;
+
+use crate::{match_def_path, must_use_attr};
+
+pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+    ty.is_copy_modulo_regions(cx.tcx.at(DUMMY_SP), cx.param_env)
+}
+
+/// Checks whether a type can be partially moved.
+pub fn can_partially_move_ty(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+    if has_drop(cx, ty) || is_copy(cx, ty) {
+        return false;
+    }
+    match ty.kind() {
+        ty::Param(_) => false,
+        ty::Adt(def, subs) => def.all_fields().any(|f| !is_copy(cx, f.ty(cx.tcx, subs))),
+        _ => true,
+    }
+}
+
+/// Walks into `ty` and returns `true` if any inner type is the same as `other_ty`
+pub fn contains_ty(ty: Ty<'_>, other_ty: Ty<'_>) -> bool {
+    ty.walk().any(|inner| match inner.unpack() {
+        GenericArgKind::Type(inner_ty) => ty::TyS::same_type(other_ty, inner_ty),
+        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
+    })
+}
+
+/// Walks into `ty` and returns `true` if any inner type is an instance of the given adt
+/// constructor.
+pub fn contains_adt_constructor(ty: Ty<'_>, adt: &AdtDef) -> bool {
+    ty.walk().any(|inner| match inner.unpack() {
+        GenericArgKind::Type(inner_ty) => inner_ty.ty_adt_def() == Some(adt),
+        GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
+    })
+}
+
+/// Returns true if ty has `iter` or `iter_mut` methods
+pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<Symbol> {
+    // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
+    // exists and has the desired signature. Unfortunately FnCtxt is not exported
+    // so we can't use its `lookup_method` method.
+    let into_iter_collections: &[Symbol] = &[
+        sym::vec_type,
+        sym::option_type,
+        sym::result_type,
+        sym::BTreeMap,
+        sym::BTreeSet,
+        sym::vecdeque_type,
+        sym::LinkedList,
+        sym::BinaryHeap,
+        sym::hashset_type,
+        sym::hashmap_type,
+        sym::PathBuf,
+        sym::Path,
+        sym::Receiver,
+    ];
+
+    let ty_to_check = match probably_ref_ty.kind() {
+        ty::Ref(_, ty_to_check, _) => ty_to_check,
+        _ => probably_ref_ty,
+    };
+
+    let def_id = match ty_to_check.kind() {
+        ty::Array(..) => return Some(sym::array),
+        ty::Slice(..) => return Some(sym::slice),
+        ty::Adt(adt, _) => adt.did,
+        _ => return None,
+    };
+
+    for &name in into_iter_collections {
+        if cx.tcx.is_diagnostic_item(name, def_id) {
+            return Some(cx.tcx.item_name(def_id));
+        }
+    }
+    None
+}
+
+/// Checks whether a type implements a trait.
+/// See also `get_trait_def_id`.
+pub fn implements_trait<'tcx>(
+    cx: &LateContext<'tcx>,
+    ty: Ty<'tcx>,
+    trait_id: DefId,
+    ty_params: &[GenericArg<'tcx>],
+) -> bool {
+    // Do not check on infer_types to avoid panic in evaluate_obligation.
+    if ty.has_infer_types() {
+        return false;
+    }
+    let ty = cx.tcx.erase_regions(ty);
+    if ty.has_escaping_bound_vars() {
+        return false;
+    }
+    let ty_params = cx.tcx.mk_substs(ty_params.iter());
+    cx.tcx.type_implements_trait((trait_id, ty, ty_params, cx.param_env))
+}
+
+/// Checks whether this type implements `Drop`.
+pub fn has_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+    match ty.ty_adt_def() {
+        Some(def) => def.has_dtor(cx.tcx),
+        None => false,
+    }
+}
+
+// Returns whether the type has #[must_use] attribute
+pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+    match ty.kind() {
+        ty::Adt(ref adt, _) => must_use_attr(&cx.tcx.get_attrs(adt.did)).is_some(),
+        ty::Foreign(ref did) => must_use_attr(&cx.tcx.get_attrs(*did)).is_some(),
+        ty::Slice(ref ty)
+        | ty::Array(ref ty, _)
+        | ty::RawPtr(ty::TypeAndMut { ref ty, .. })
+        | ty::Ref(_, ref ty, _) => {
+            // for the Array case we don't need to care for the len == 0 case
+            // because we don't want to lint functions returning empty arrays
+            is_must_use_ty(cx, *ty)
+        },
+        ty::Tuple(ref substs) => substs.types().any(|ty| is_must_use_ty(cx, ty)),
+        ty::Opaque(ref def_id, _) => {
+            for (predicate, _) in cx.tcx.explicit_item_bounds(*def_id) {
+                if let ty::PredicateKind::Trait(trait_predicate, _) = predicate.kind().skip_binder() {
+                    if must_use_attr(&cx.tcx.get_attrs(trait_predicate.trait_ref.def_id)).is_some() {
+                        return true;
+                    }
+                }
+            }
+            false
+        },
+        ty::Dynamic(binder, _) => {
+            for predicate in binder.iter() {
+                if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() {
+                    if must_use_attr(&cx.tcx.get_attrs(trait_ref.def_id)).is_some() {
+                        return true;
+                    }
+                }
+            }
+            false
+        },
+        _ => false,
+    }
+}
+
+// FIXME: Per https://doc.rust-lang.org/nightly/nightly-rustc/rustc_trait_selection/infer/at/struct.At.html#method.normalize
+// this function can be removed once the `normalizie` method does not panic when normalization does
+// not succeed
+/// Checks if `Ty` is normalizable. This function is useful
+/// to avoid crashes on `layout_of`.
+pub fn is_normalizable<'tcx>(cx: &LateContext<'tcx>, param_env: ty::ParamEnv<'tcx>, ty: Ty<'tcx>) -> bool {
+    is_normalizable_helper(cx, param_env, ty, &mut HashMap::new())
+}
+
+fn is_normalizable_helper<'tcx>(
+    cx: &LateContext<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    ty: Ty<'tcx>,
+    cache: &mut HashMap<Ty<'tcx>, bool>,
+) -> bool {
+    if let Some(&cached_result) = cache.get(ty) {
+        return cached_result;
+    }
+    // prevent recursive loops, false-negative is better than endless loop leading to stack overflow
+    cache.insert(ty, false);
+    let result = cx.tcx.infer_ctxt().enter(|infcx| {
+        let cause = rustc_middle::traits::ObligationCause::dummy();
+        if infcx.at(&cause, param_env).normalize(ty).is_ok() {
+            match ty.kind() {
+                ty::Adt(def, substs) => def.variants.iter().all(|variant| {
+                    variant
+                        .fields
+                        .iter()
+                        .all(|field| is_normalizable_helper(cx, param_env, field.ty(cx.tcx, substs), cache))
+                }),
+                _ => ty.walk().all(|generic_arg| match generic_arg.unpack() {
+                    GenericArgKind::Type(inner_ty) if inner_ty != ty => {
+                        is_normalizable_helper(cx, param_env, inner_ty, cache)
+                    },
+                    _ => true, // if inner_ty == ty, we've already checked it
+                }),
+            }
+        } else {
+            false
+        }
+    });
+    cache.insert(ty, result);
+    result
+}
+
+/// Returns true iff the given type is a primitive (a bool or char, any integer or floating-point
+/// number type, a str, or an array, slice, or tuple of those types).
+pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
+    match ty.kind() {
+        ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => true,
+        ty::Ref(_, inner, _) if *inner.kind() == ty::Str => true,
+        ty::Array(inner_type, _) | ty::Slice(inner_type) => is_recursively_primitive_type(inner_type),
+        ty::Tuple(inner_types) => inner_types.types().all(is_recursively_primitive_type),
+        _ => false,
+    }
+}
+
+/// Checks if the type is equal to a diagnostic item
+///
+/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
+pub fn is_type_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
+    match ty.kind() {
+        ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did),
+        _ => false,
+    }
+}
+
+/// Checks if the type is equal to a lang item
+pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: hir::LangItem) -> bool {
+    match ty.kind() {
+        ty::Adt(adt, _) => cx.tcx.lang_items().require(lang_item).unwrap() == adt.did,
+        _ => false,
+    }
+}
+
+/// Return `true` if the passed `typ` is `isize` or `usize`.
+pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
+    matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
+}
+
+/// Checks if type is struct, enum or union type with the given def path.
+///
+/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
+/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
+pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
+    match ty.kind() {
+        ty::Adt(adt, _) => match_def_path(cx, adt.did, path),
+        _ => false,
+    }
+}
+
+/// Peels off all references on the type. Returns the underlying type and the number of references
+/// removed.
+pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
+    fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
+        if let ty::Ref(_, ty, _) = ty.kind() {
+            peel(ty, count + 1)
+        } else {
+            (ty, count)
+        }
+    }
+    peel(ty, 0)
+}
+
+/// Peels off all references on the type.Returns the underlying type, the number of references
+/// removed, and whether the pointer is ultimately mutable or not.
+pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
+    fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
+        match ty.kind() {
+            ty::Ref(_, ty, Mutability::Mut) => f(ty, count + 1, mutability),
+            ty::Ref(_, ty, Mutability::Not) => f(ty, count + 1, Mutability::Not),
+            _ => (ty, count, mutability),
+        }
+    }
+    f(ty, 0, Mutability::Mut)
+}
+
+/// Returns `true` if the given type is an `unsafe` function.
+pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+    match ty.kind() {
+        ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).unsafety() == Unsafety::Unsafe,
+        _ => false,
+    }
+}
+
+/// Returns the base type for HIR references and pointers.
+pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
+    match ty.kind {
+        TyKind::Ptr(ref mut_ty) | TyKind::Rptr(_, ref mut_ty) => walk_ptrs_hir_ty(&mut_ty.ty),
+        _ => ty,
+    }
+}
+
+/// Returns the base type for references and raw pointers, and count reference
+/// depth.
+pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
+    fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
+        match ty.kind() {
+            ty::Ref(_, ty, _) => inner(ty, depth + 1),
+            _ => (ty, depth),
+        }
+    }
+    inner(ty, 0)
+}
index 0b1ab6b7ea188407e64ea32271737365e7903ef6..54f110988d7352f63e5b75ea32f776d5e58ab2c0 100644 (file)
@@ -7,8 +7,8 @@
 use rustc_hir::{Expr, ExprKind, HirId, Path};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
-use rustc_middle::mir::FakeReadCause;
 use rustc_middle::hir::map::Map;
+use rustc_middle::mir::FakeReadCause;
 use rustc_middle::ty;
 use rustc_typeck::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 
@@ -79,7 +79,7 @@ fn mutate(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId) {
         self.update(&cmt)
     }
 
-    fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _:HirId) { }
+    fn fake_read(&mut self, _: rustc_typeck::expr_use_visitor::Place<'tcx>, _: FakeReadCause, _: HirId) {}
 }
 
 pub struct ParamBindingIdCollector {
index c56e84e2e32a22569c954c869506e56a0787b71d..5226875cc210a8c7989efd09a6aa7de31a9d7ccb 100644 (file)
@@ -88,8 +88,8 @@ cargo dev fmt
 cargo dev update_lints
 # create a new lint and register it
 cargo dev new_lint
-# (experimental) Setup Clippy to work with rust-analyzer
-cargo dev ra_setup
+# (experimental) Setup Clippy to work with IntelliJ-Rust
+cargo dev ide_setup
 ```
 
 ## lintcheck
index c52a7f2e74321884b633f1e0b89f3932fc3ee83f..c2821f31fd7c30ae38fb66c8790db02d9a4875bd 100644 (file)
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2021-03-11"
+channel = "nightly-2021-03-25"
 components = ["llvm-tools-preview", "rustc-dev", "rust-src"]
index 735909887acb189ac97b01a63708ae738989a2b1..c6659edacc32bab31d320cac5504c4d6888ba0a8 100644 (file)
@@ -20,4 +20,9 @@ enum Flags {
 // `GccLlvmSomething`
 struct GCCLLVMSomething;
 
+// don't warn on public items
+pub struct MIXEDCapital;
+
+pub struct FULLCAPITAL;
+
 fn main() {}
index ed87a50547d17398a13790c1ab9ad7e4114aabe0..702684f6b43a6467067f02a9780df538435f81bf 100644 (file)
@@ -1,10 +1,11 @@
-error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
   --> $DIR/def_id_nocore.rs:26:19
    |
 LL |     pub fn as_ref(self) -> &'static str {
    |                   ^^^^
    |
    = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
+   = help: consider choosing a less ambiguous name
 
 error: aborting due to previous error
 
diff --git a/tests/ui/dereference.fixed b/tests/ui/dereference.fixed
deleted file mode 100644 (file)
index 459ca91..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-// run-rustfix
-
-#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
-#![warn(clippy::explicit_deref_methods)]
-
-use std::ops::{Deref, DerefMut};
-
-fn concat(deref_str: &str) -> String {
-    format!("{}bar", deref_str)
-}
-
-fn just_return(deref_str: &str) -> &str {
-    deref_str
-}
-
-struct CustomVec(Vec<u8>);
-impl Deref for CustomVec {
-    type Target = Vec<u8>;
-
-    fn deref(&self) -> &Vec<u8> {
-        &self.0
-    }
-}
-
-fn main() {
-    let a: &mut String = &mut String::from("foo");
-
-    // these should require linting
-
-    let b: &str = &*a;
-
-    let b: &mut str = &mut *a;
-
-    // both derefs should get linted here
-    let b: String = format!("{}, {}", &*a, &*a);
-
-    println!("{}", &*a);
-
-    #[allow(clippy::match_single_binding)]
-    match &*a {
-        _ => (),
-    }
-
-    let b: String = concat(&*a);
-
-    let b = &*just_return(a);
-
-    let b: String = concat(&*just_return(a));
-
-    let b: &str = &*a.deref();
-
-    let opt_a = Some(a.clone());
-    let b = &*opt_a.unwrap();
-
-    // following should not require linting
-
-    let cv = CustomVec(vec![0, 42]);
-    let c = cv.deref()[0];
-
-    let b: &str = &*a.deref();
-
-    let b: String = a.deref().clone();
-
-    let b: usize = a.deref_mut().len();
-
-    let b: &usize = &a.deref().len();
-
-    let b: &str = &*a;
-
-    let b: &mut str = &mut *a;
-
-    macro_rules! expr_deref {
-        ($body:expr) => {
-            $body.deref()
-        };
-    }
-    let b: &str = expr_deref!(a);
-
-    // The struct does not implement Deref trait
-    #[derive(Copy, Clone)]
-    struct NoLint(u32);
-    impl NoLint {
-        pub fn deref(self) -> u32 {
-            self.0
-        }
-        pub fn deref_mut(self) -> u32 {
-            self.0
-        }
-    }
-    let no_lint = NoLint(42);
-    let b = no_lint.deref();
-    let b = no_lint.deref_mut();
-}
diff --git a/tests/ui/dereference.rs b/tests/ui/dereference.rs
deleted file mode 100644 (file)
index 8dc5272..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-// run-rustfix
-
-#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
-#![warn(clippy::explicit_deref_methods)]
-
-use std::ops::{Deref, DerefMut};
-
-fn concat(deref_str: &str) -> String {
-    format!("{}bar", deref_str)
-}
-
-fn just_return(deref_str: &str) -> &str {
-    deref_str
-}
-
-struct CustomVec(Vec<u8>);
-impl Deref for CustomVec {
-    type Target = Vec<u8>;
-
-    fn deref(&self) -> &Vec<u8> {
-        &self.0
-    }
-}
-
-fn main() {
-    let a: &mut String = &mut String::from("foo");
-
-    // these should require linting
-
-    let b: &str = a.deref();
-
-    let b: &mut str = a.deref_mut();
-
-    // both derefs should get linted here
-    let b: String = format!("{}, {}", a.deref(), a.deref());
-
-    println!("{}", a.deref());
-
-    #[allow(clippy::match_single_binding)]
-    match a.deref() {
-        _ => (),
-    }
-
-    let b: String = concat(a.deref());
-
-    let b = just_return(a).deref();
-
-    let b: String = concat(just_return(a).deref());
-
-    let b: &str = a.deref().deref();
-
-    let opt_a = Some(a.clone());
-    let b = opt_a.unwrap().deref();
-
-    // following should not require linting
-
-    let cv = CustomVec(vec![0, 42]);
-    let c = cv.deref()[0];
-
-    let b: &str = &*a.deref();
-
-    let b: String = a.deref().clone();
-
-    let b: usize = a.deref_mut().len();
-
-    let b: &usize = &a.deref().len();
-
-    let b: &str = &*a;
-
-    let b: &mut str = &mut *a;
-
-    macro_rules! expr_deref {
-        ($body:expr) => {
-            $body.deref()
-        };
-    }
-    let b: &str = expr_deref!(a);
-
-    // The struct does not implement Deref trait
-    #[derive(Copy, Clone)]
-    struct NoLint(u32);
-    impl NoLint {
-        pub fn deref(self) -> u32 {
-            self.0
-        }
-        pub fn deref_mut(self) -> u32 {
-            self.0
-        }
-    }
-    let no_lint = NoLint(42);
-    let b = no_lint.deref();
-    let b = no_lint.deref_mut();
-}
diff --git a/tests/ui/dereference.stderr b/tests/ui/dereference.stderr
deleted file mode 100644 (file)
index d26b462..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-error: explicit deref method call
-  --> $DIR/dereference.rs:30:19
-   |
-LL |     let b: &str = a.deref();
-   |                   ^^^^^^^^^ help: try this: `&*a`
-   |
-   = note: `-D clippy::explicit-deref-methods` implied by `-D warnings`
-
-error: explicit deref_mut method call
-  --> $DIR/dereference.rs:32:23
-   |
-LL |     let b: &mut str = a.deref_mut();
-   |                       ^^^^^^^^^^^^^ help: try this: `&mut *a`
-
-error: explicit deref method call
-  --> $DIR/dereference.rs:35:39
-   |
-LL |     let b: String = format!("{}, {}", a.deref(), a.deref());
-   |                                       ^^^^^^^^^ help: try this: `&*a`
-
-error: explicit deref method call
-  --> $DIR/dereference.rs:35:50
-   |
-LL |     let b: String = format!("{}, {}", a.deref(), a.deref());
-   |                                                  ^^^^^^^^^ help: try this: `&*a`
-
-error: explicit deref method call
-  --> $DIR/dereference.rs:37:20
-   |
-LL |     println!("{}", a.deref());
-   |                    ^^^^^^^^^ help: try this: `&*a`
-
-error: explicit deref method call
-  --> $DIR/dereference.rs:40:11
-   |
-LL |     match a.deref() {
-   |           ^^^^^^^^^ help: try this: `&*a`
-
-error: explicit deref method call
-  --> $DIR/dereference.rs:44:28
-   |
-LL |     let b: String = concat(a.deref());
-   |                            ^^^^^^^^^ help: try this: `&*a`
-
-error: explicit deref method call
-  --> $DIR/dereference.rs:46:13
-   |
-LL |     let b = just_return(a).deref();
-   |             ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)`
-
-error: explicit deref method call
-  --> $DIR/dereference.rs:48:28
-   |
-LL |     let b: String = concat(just_return(a).deref());
-   |                            ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*just_return(a)`
-
-error: explicit deref method call
-  --> $DIR/dereference.rs:50:19
-   |
-LL |     let b: &str = a.deref().deref();
-   |                   ^^^^^^^^^^^^^^^^^ help: try this: `&*a.deref()`
-
-error: explicit deref method call
-  --> $DIR/dereference.rs:53:13
-   |
-LL |     let b = opt_a.unwrap().deref();
-   |             ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()`
-
-error: aborting due to 11 previous errors
-
diff --git a/tests/ui/explicit_deref_methods.fixed b/tests/ui/explicit_deref_methods.fixed
new file mode 100644 (file)
index 0000000..51d0468
--- /dev/null
@@ -0,0 +1,95 @@
+// run-rustfix
+
+#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
+#![warn(clippy::explicit_deref_methods)]
+
+use std::ops::{Deref, DerefMut};
+
+fn concat(deref_str: &str) -> String {
+    format!("{}bar", deref_str)
+}
+
+fn just_return(deref_str: &str) -> &str {
+    deref_str
+}
+
+struct CustomVec(Vec<u8>);
+impl Deref for CustomVec {
+    type Target = Vec<u8>;
+
+    fn deref(&self) -> &Vec<u8> {
+        &self.0
+    }
+}
+
+fn main() {
+    let a: &mut String = &mut String::from("foo");
+
+    // these should require linting
+
+    let b: &str = &*a;
+
+    let b: &mut str = &mut **a;
+
+    // both derefs should get linted here
+    let b: String = format!("{}, {}", &*a, &*a);
+
+    println!("{}", &*a);
+
+    #[allow(clippy::match_single_binding)]
+    match &*a {
+        _ => (),
+    }
+
+    let b: String = concat(&*a);
+
+    let b = just_return(a);
+
+    let b: String = concat(just_return(a));
+
+    let b: &str = &**a;
+
+    let opt_a = Some(a.clone());
+    let b = &*opt_a.unwrap();
+
+    // following should not require linting
+
+    let cv = CustomVec(vec![0, 42]);
+    let c = cv.deref()[0];
+
+    let b: &str = &*a.deref();
+
+    let b: String = a.deref().clone();
+
+    let b: usize = a.deref_mut().len();
+
+    let b: &usize = &a.deref().len();
+
+    let b: &str = &*a;
+
+    let b: &mut str = &mut *a;
+
+    macro_rules! expr_deref {
+        ($body:expr) => {
+            $body.deref()
+        };
+    }
+    let b: &str = expr_deref!(a);
+
+    let b: &str = expr_deref!(&*a);
+
+    // The struct does not implement Deref trait
+    #[derive(Copy, Clone)]
+    struct NoLint(u32);
+    impl NoLint {
+        pub fn deref(self) -> u32 {
+            self.0
+        }
+        pub fn deref_mut(self) -> u32 {
+            self.0
+        }
+    }
+    let no_lint = NoLint(42);
+    let b = no_lint.deref();
+    let b = no_lint.deref_mut();
+}
diff --git a/tests/ui/explicit_deref_methods.rs b/tests/ui/explicit_deref_methods.rs
new file mode 100644 (file)
index 0000000..680664b
--- /dev/null
@@ -0,0 +1,95 @@
+// run-rustfix
+
+#![allow(unused_variables, clippy::many_single_char_names, clippy::clone_double_ref)]
+#![warn(clippy::explicit_deref_methods)]
+
+use std::ops::{Deref, DerefMut};
+
+fn concat(deref_str: &str) -> String {
+    format!("{}bar", deref_str)
+}
+
+fn just_return(deref_str: &str) -> &str {
+    deref_str
+}
+
+struct CustomVec(Vec<u8>);
+impl Deref for CustomVec {
+    type Target = Vec<u8>;
+
+    fn deref(&self) -> &Vec<u8> {
+        &self.0
+    }
+}
+
+fn main() {
+    let a: &mut String = &mut String::from("foo");
+
+    // these should require linting
+
+    let b: &str = a.deref();
+
+    let b: &mut str = a.deref_mut();
+
+    // both derefs should get linted here
+    let b: String = format!("{}, {}", a.deref(), a.deref());
+
+    println!("{}", a.deref());
+
+    #[allow(clippy::match_single_binding)]
+    match a.deref() {
+        _ => (),
+    }
+
+    let b: String = concat(a.deref());
+
+    let b = just_return(a).deref();
+
+    let b: String = concat(just_return(a).deref());
+
+    let b: &str = a.deref().deref();
+
+    let opt_a = Some(a.clone());
+    let b = opt_a.unwrap().deref();
+
+    // following should not require linting
+
+    let cv = CustomVec(vec![0, 42]);
+    let c = cv.deref()[0];
+
+    let b: &str = &*a.deref();
+
+    let b: String = a.deref().clone();
+
+    let b: usize = a.deref_mut().len();
+
+    let b: &usize = &a.deref().len();
+
+    let b: &str = &*a;
+
+    let b: &mut str = &mut *a;
+
+    macro_rules! expr_deref {
+        ($body:expr) => {
+            $body.deref()
+        };
+    }
+    let b: &str = expr_deref!(a);
+
+    let b: &str = expr_deref!(a.deref());
+
+    // The struct does not implement Deref trait
+    #[derive(Copy, Clone)]
+    struct NoLint(u32);
+    impl NoLint {
+        pub fn deref(self) -> u32 {
+            self.0
+        }
+        pub fn deref_mut(self) -> u32 {
+            self.0
+        }
+    }
+    let no_lint = NoLint(42);
+    let b = no_lint.deref();
+    let b = no_lint.deref_mut();
+}
diff --git a/tests/ui/explicit_deref_methods.stderr b/tests/ui/explicit_deref_methods.stderr
new file mode 100644 (file)
index 0000000..8035d77
--- /dev/null
@@ -0,0 +1,76 @@
+error: explicit `deref` method call
+  --> $DIR/explicit_deref_methods.rs:30:19
+   |
+LL |     let b: &str = a.deref();
+   |                   ^^^^^^^^^ help: try this: `&*a`
+   |
+   = note: `-D clippy::explicit-deref-methods` implied by `-D warnings`
+
+error: explicit `deref_mut` method call
+  --> $DIR/explicit_deref_methods.rs:32:23
+   |
+LL |     let b: &mut str = a.deref_mut();
+   |                       ^^^^^^^^^^^^^ help: try this: `&mut **a`
+
+error: explicit `deref` method call
+  --> $DIR/explicit_deref_methods.rs:35:39
+   |
+LL |     let b: String = format!("{}, {}", a.deref(), a.deref());
+   |                                       ^^^^^^^^^ help: try this: `&*a`
+
+error: explicit `deref` method call
+  --> $DIR/explicit_deref_methods.rs:35:50
+   |
+LL |     let b: String = format!("{}, {}", a.deref(), a.deref());
+   |                                                  ^^^^^^^^^ help: try this: `&*a`
+
+error: explicit `deref` method call
+  --> $DIR/explicit_deref_methods.rs:37:20
+   |
+LL |     println!("{}", a.deref());
+   |                    ^^^^^^^^^ help: try this: `&*a`
+
+error: explicit `deref` method call
+  --> $DIR/explicit_deref_methods.rs:40:11
+   |
+LL |     match a.deref() {
+   |           ^^^^^^^^^ help: try this: `&*a`
+
+error: explicit `deref` method call
+  --> $DIR/explicit_deref_methods.rs:44:28
+   |
+LL |     let b: String = concat(a.deref());
+   |                            ^^^^^^^^^ help: try this: `&*a`
+
+error: explicit `deref` method call
+  --> $DIR/explicit_deref_methods.rs:46:13
+   |
+LL |     let b = just_return(a).deref();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)`
+
+error: explicit `deref` method call
+  --> $DIR/explicit_deref_methods.rs:48:28
+   |
+LL |     let b: String = concat(just_return(a).deref());
+   |                            ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)`
+
+error: explicit `deref` method call
+  --> $DIR/explicit_deref_methods.rs:50:19
+   |
+LL |     let b: &str = a.deref().deref();
+   |                   ^^^^^^^^^^^^^^^^^ help: try this: `&**a`
+
+error: explicit `deref` method call
+  --> $DIR/explicit_deref_methods.rs:53:13
+   |
+LL |     let b = opt_a.unwrap().deref();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()`
+
+error: explicit `deref` method call
+  --> $DIR/explicit_deref_methods.rs:79:31
+   |
+LL |     let b: &str = expr_deref!(a.deref());
+   |                               ^^^^^^^^^ help: try this: `&*a`
+
+error: aborting due to 12 previous errors
+
index 9fc208f5332a5ccbb9e9567425bac44d2ffb1dd7..1368c5d79848032b423bb3f6520655a8e6567bf5 100644 (file)
@@ -136,6 +136,13 @@ fn main() {
 
     // Don't lint in external macros
     field_reassign_with_default!();
+
+    // be sure suggestion is correct with generics
+    let mut a: Wrapper<bool> = Default::default();
+    a.i = true;
+
+    let mut a: WrapperMulti<i32, i64> = Default::default();
+    a.i = 42;
 }
 
 mod m {
@@ -145,3 +152,14 @@ pub struct F {
         b: u64,
     }
 }
+
+#[derive(Default)]
+struct Wrapper<T> {
+    i: T,
+}
+
+#[derive(Default)]
+struct WrapperMulti<T, U> {
+    i: T,
+    j: U,
+}
index 2f0f28f7bb724f92422ce0e618423342880d8f1f..dd7c0360bb1e2bc40355e216bec4fad739615f43 100644 (file)
@@ -83,5 +83,29 @@ note: consider initializing the variable with `C { i: vec![1], ..Default::defaul
 LL |     let mut a: C = C::default();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 7 previous errors
+error: field assignment outside of initializer for an instance created with Default::default()
+  --> $DIR/field_reassign_with_default.rs:142:5
+   |
+LL |     a.i = true;
+   |     ^^^^^^^^^^^
+   |
+note: consider initializing the variable with `Wrapper::<bool> { i: true }` and removing relevant reassignments
+  --> $DIR/field_reassign_with_default.rs:141:5
+   |
+LL |     let mut a: Wrapper<bool> = Default::default();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: field assignment outside of initializer for an instance created with Default::default()
+  --> $DIR/field_reassign_with_default.rs:145:5
+   |
+LL |     a.i = 42;
+   |     ^^^^^^^^^
+   |
+note: consider initializing the variable with `WrapperMulti::<i32, i64> { i: 42, ..Default::default() }` and removing relevant reassignments
+  --> $DIR/field_reassign_with_default.rs:144:5
+   |
+LL |     let mut a: WrapperMulti<i32, i64> = Default::default();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 9 previous errors
 
diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs
new file mode 100644 (file)
index 0000000..54789bf
--- /dev/null
@@ -0,0 +1,104 @@
+#![warn(clippy::if_then_some_else_none)]
+#![feature(custom_inner_attributes)]
+
+fn main() {
+    // Should issue an error.
+    let _ = if foo() {
+        println!("true!");
+        Some("foo")
+    } else {
+        None
+    };
+
+    // Should issue an error when macros are used.
+    let _ = if matches!(true, true) {
+        println!("true!");
+        Some(matches!(true, false))
+    } else {
+        None
+    };
+
+    // Should issue an error. Binary expression `o < 32` should be parenthesized.
+    let x = Some(5);
+    let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
+
+    // Should issue an error. Unary expression `!x` should be parenthesized.
+    let x = true;
+    let _ = if !x { Some(0) } else { None };
+
+    // Should not issue an error since the `else` block has a statement besides `None`.
+    let _ = if foo() {
+        println!("true!");
+        Some("foo")
+    } else {
+        eprintln!("false...");
+        None
+    };
+
+    // Should not issue an error since there are more than 2 blocks in the if-else chain.
+    let _ = if foo() {
+        println!("foo true!");
+        Some("foo")
+    } else if bar() {
+        println!("bar true!");
+        Some("bar")
+    } else {
+        None
+    };
+
+    let _ = if foo() {
+        println!("foo true!");
+        Some("foo")
+    } else {
+        bar().then(|| {
+            println!("bar true!");
+            "bar"
+        })
+    };
+
+    // Should not issue an error since the `then` block has `None`, not `Some`.
+    let _ = if foo() { None } else { Some("foo is false") };
+
+    // Should not issue an error since the `else` block doesn't use `None` directly.
+    let _ = if foo() { Some("foo is true") } else { into_none() };
+
+    // Should not issue an error since the `then` block doesn't use `Some` directly.
+    let _ = if foo() { into_some("foo") } else { None };
+}
+
+fn _msrv_1_49() {
+    #![clippy::msrv = "1.49"]
+    // `bool::then` was stabilized in 1.50. Do not lint this
+    let _ = if foo() {
+        println!("true!");
+        Some(149)
+    } else {
+        None
+    };
+}
+
+fn _msrv_1_50() {
+    #![clippy::msrv = "1.50"]
+    let _ = if foo() {
+        println!("true!");
+        Some(150)
+    } else {
+        None
+    };
+}
+
+fn foo() -> bool {
+    unimplemented!()
+}
+
+fn bar() -> bool {
+    unimplemented!()
+}
+
+fn into_some<T>(v: T) -> Option<T> {
+    Some(v)
+}
+
+fn into_none<T>() -> Option<T> {
+    None
+}
diff --git a/tests/ui/if_then_some_else_none.stderr b/tests/ui/if_then_some_else_none.stderr
new file mode 100644 (file)
index 0000000..8cb22d5
--- /dev/null
@@ -0,0 +1,61 @@
+error: this could be simplified with `bool::then`
+  --> $DIR/if_then_some_else_none.rs:6:13
+   |
+LL |       let _ = if foo() {
+   |  _____________^
+LL | |         println!("true!");
+LL | |         Some("foo")
+LL | |     } else {
+LL | |         None
+LL | |     };
+   | |_____^
+   |
+   = note: `-D clippy::if-then-some-else-none` implied by `-D warnings`
+   = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ "foo" })`
+
+error: this could be simplified with `bool::then`
+  --> $DIR/if_then_some_else_none.rs:14:13
+   |
+LL |       let _ = if matches!(true, true) {
+   |  _____________^
+LL | |         println!("true!");
+LL | |         Some(matches!(true, false))
+LL | |     } else {
+LL | |         None
+LL | |     };
+   | |_____^
+   |
+   = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })`
+
+error: this could be simplified with `bool::then`
+  --> $DIR/if_then_some_else_none.rs:23:28
+   |
+LL |     let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using `bool::then` like: `(o < 32).then(|| o)`
+
+error: this could be simplified with `bool::then`
+  --> $DIR/if_then_some_else_none.rs:27:13
+   |
+LL |     let _ = if !x { Some(0) } else { None };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider using `bool::then` like: `(!x).then(|| 0)`
+
+error: this could be simplified with `bool::then`
+  --> $DIR/if_then_some_else_none.rs:82:13
+   |
+LL |       let _ = if foo() {
+   |  _____________^
+LL | |         println!("true!");
+LL | |         Some(150)
+LL | |     } else {
+LL | |         None
+LL | |     };
+   | |_____^
+   |
+   = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ 150 })`
+
+error: aborting due to 5 previous errors
+
index d7abe44f2540834bb8dc842af33f3eefb341dc26..d021bb19579f4baa6c3d4cd5fa8fd9e58c4f7c1c 100644 (file)
@@ -1,4 +1,4 @@
-error: inconsistent struct constructor
+error: struct constructor field order is inconsistent with struct definition field order
   --> $DIR/inconsistent_struct_constructor.rs:25:9
    |
 LL |         Foo { y, x, z };
@@ -6,7 +6,7 @@ LL |         Foo { y, x, z };
    |
    = note: `-D clippy::inconsistent-struct-constructor` implied by `-D warnings`
 
-error: inconsistent struct constructor
+error: struct constructor field order is inconsistent with struct definition field order
   --> $DIR/inconsistent_struct_constructor.rs:43:9
    |
 LL | /         Foo {
index cff68eca933743338acd6b184c3b25c0883aa9c1..b5bd35a68785a9242e67f9b964b4822ef0d9eaae 100644 (file)
@@ -1,4 +1,5 @@
 #![warn(clippy::manual_flatten)]
+#![allow(clippy::useless_vec)]
 
 fn main() {
     // Test for loop over implicitly adjusted `Iterator` with `if let` expression
@@ -69,6 +70,27 @@ fn main() {
         }
     }
 
+    let vec_of_ref = vec![&Some(1)];
+    for n in &vec_of_ref {
+        if let Some(n) = n {
+            println!("{:?}", n);
+        }
+    }
+
+    let vec_of_ref = &vec_of_ref;
+    for n in vec_of_ref {
+        if let Some(n) = n {
+            println!("{:?}", n);
+        }
+    }
+
+    let slice_of_ref = &[&Some(1)];
+    for n in slice_of_ref {
+        if let Some(n) = n {
+            println!("{:?}", n);
+        }
+    }
+
     // Using manual flatten should not trigger the lint
     for n in vec![Some(1), Some(2), Some(3)].iter().flatten() {
         println!("{}", n);
index 855dd9130e2fbbdc8e9c77047a4d69d1c5ebae9e..be5f8a1d818847b8ce672df247d5e218d80bbd71 100644 (file)
@@ -1,5 +1,5 @@
 error: unnecessary `if let` since only the `Some` variant of the iterator element is used
-  --> $DIR/manual_flatten.rs:6:5
+  --> $DIR/manual_flatten.rs:7:5
    |
 LL |       for n in x {
    |       ^        - help: try: `x.into_iter().flatten()`
@@ -13,7 +13,7 @@ LL | |     }
    |
    = note: `-D clippy::manual-flatten` implied by `-D warnings`
 help: ...and remove the `if let` statement in the for loop
-  --> $DIR/manual_flatten.rs:7:9
+  --> $DIR/manual_flatten.rs:8:9
    |
 LL | /         if let Some(y) = n {
 LL | |             println!("{}", y);
@@ -21,7 +21,7 @@ LL | |         }
    | |_________^
 
 error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
-  --> $DIR/manual_flatten.rs:14:5
+  --> $DIR/manual_flatten.rs:15:5
    |
 LL |       for n in y.clone() {
    |       ^        --------- help: try: `y.clone().into_iter().flatten()`
@@ -34,7 +34,7 @@ LL | |     }
    | |_____^
    |
 help: ...and remove the `if let` statement in the for loop
-  --> $DIR/manual_flatten.rs:15:9
+  --> $DIR/manual_flatten.rs:16:9
    |
 LL | /         if let Ok(n) = n {
 LL | |             println!("{}", n);
@@ -42,7 +42,7 @@ LL | |         };
    | |_________^
 
 error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
-  --> $DIR/manual_flatten.rs:21:5
+  --> $DIR/manual_flatten.rs:22:5
    |
 LL |       for n in &y {
    |       ^        -- help: try: `y.iter().flatten()`
@@ -55,7 +55,7 @@ LL | |     }
    | |_____^
    |
 help: ...and remove the `if let` statement in the for loop
-  --> $DIR/manual_flatten.rs:22:9
+  --> $DIR/manual_flatten.rs:23:9
    |
 LL | /         if let Ok(n) = n {
 LL | |             println!("{}", n);
@@ -63,7 +63,7 @@ LL | |         }
    | |_________^
 
 error: unnecessary `if let` since only the `Ok` variant of the iterator element is used
-  --> $DIR/manual_flatten.rs:31:5
+  --> $DIR/manual_flatten.rs:32:5
    |
 LL |       for n in z {
    |       ^        - help: try: `z.into_iter().flatten()`
@@ -76,7 +76,7 @@ LL | |     }
    | |_____^
    |
 help: ...and remove the `if let` statement in the for loop
-  --> $DIR/manual_flatten.rs:32:9
+  --> $DIR/manual_flatten.rs:33:9
    |
 LL | /         if let Ok(n) = n {
 LL | |             println!("{}", n);
@@ -84,7 +84,7 @@ LL | |         }
    | |_________^
 
 error: unnecessary `if let` since only the `Some` variant of the iterator element is used
-  --> $DIR/manual_flatten.rs:40:5
+  --> $DIR/manual_flatten.rs:41:5
    |
 LL |       for n in z {
    |       ^        - help: try: `z.flatten()`
@@ -97,12 +97,75 @@ LL | |     }
    | |_____^
    |
 help: ...and remove the `if let` statement in the for loop
-  --> $DIR/manual_flatten.rs:41:9
+  --> $DIR/manual_flatten.rs:42:9
    |
 LL | /         if let Some(m) = n {
 LL | |             println!("{}", m);
 LL | |         }
    | |_________^
 
-error: aborting due to 5 previous errors
+error: unnecessary `if let` since only the `Some` variant of the iterator element is used
+  --> $DIR/manual_flatten.rs:74:5
+   |
+LL |       for n in &vec_of_ref {
+   |       ^        ----------- help: try: `vec_of_ref.iter().copied().flatten()`
+   |  _____|
+   | |
+LL | |         if let Some(n) = n {
+LL | |             println!("{:?}", n);
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: ...and remove the `if let` statement in the for loop
+  --> $DIR/manual_flatten.rs:75:9
+   |
+LL | /         if let Some(n) = n {
+LL | |             println!("{:?}", n);
+LL | |         }
+   | |_________^
+
+error: unnecessary `if let` since only the `Some` variant of the iterator element is used
+  --> $DIR/manual_flatten.rs:81:5
+   |
+LL |       for n in vec_of_ref {
+   |       ^        ---------- help: try: `vec_of_ref.into_iter().copied().flatten()`
+   |  _____|
+   | |
+LL | |         if let Some(n) = n {
+LL | |             println!("{:?}", n);
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: ...and remove the `if let` statement in the for loop
+  --> $DIR/manual_flatten.rs:82:9
+   |
+LL | /         if let Some(n) = n {
+LL | |             println!("{:?}", n);
+LL | |         }
+   | |_________^
+
+error: unnecessary `if let` since only the `Some` variant of the iterator element is used
+  --> $DIR/manual_flatten.rs:88:5
+   |
+LL |       for n in slice_of_ref {
+   |       ^        ------------ help: try: `slice_of_ref.into_iter().copied().flatten()`
+   |  _____|
+   | |
+LL | |         if let Some(n) = n {
+LL | |             println!("{:?}", n);
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: ...and remove the `if let` statement in the for loop
+  --> $DIR/manual_flatten.rs:89:9
+   |
+LL | /         if let Some(n) = n {
+LL | |             println!("{:?}", n);
+LL | |         }
+   | |_________^
+
+error: aborting due to 8 previous errors
 
index 9222aaf6c789c20b96f3c3effc69655187c21c26..acb6a580cebd7d5e0fb9c8ab12623a4efd0f49d8 100644 (file)
@@ -128,4 +128,9 @@ fn main() {
             None => None,
         };
     }
+
+    // #6847
+    if Some(0).is_some() {
+        Some(0)
+    } else { Some(0).map(|x| x + 1) };
 }
index 1ccb450619c69127897206289409c6fd2b32d5b4..3299e6177074c7cdefbb3c804ea7ef13a0043956 100644 (file)
@@ -186,4 +186,13 @@ async fn f3() {
             None => None,
         };
     }
+
+    // #6847
+    if let Some(_) = Some(0) {
+        Some(0)
+    } else if let Some(x) = Some(0) {
+        Some(x + 1)
+    } else {
+        None
+    };
 }
index d9f86eecd93f6cb357711f6a1c8da23777233949..048ccfb9582424f81b3201e8f4a590cd22f1cf0a 100644 (file)
@@ -172,5 +172,24 @@ LL | |         None => None,
 LL | |     };
    | |_____^ help: try this: `option_env!("").map(String::from)`
 
-error: aborting due to 19 previous errors
+error: redundant pattern matching, consider using `is_some()`
+  --> $DIR/manual_map_option.rs:191:12
+   |
+LL |     if let Some(_) = Some(0) {
+   |     -------^^^^^^^---------- help: try this: `if Some(0).is_some()`
+   |
+   = note: `-D clippy::redundant-pattern-matching` implied by `-D warnings`
+
+error: manual implementation of `Option::map`
+  --> $DIR/manual_map_option.rs:193:12
+   |
+LL |       } else if let Some(x) = Some(0) {
+   |  ____________^
+LL | |         Some(x + 1)
+LL | |     } else {
+LL | |         None
+LL | |     };
+   | |_____^ help: try this: `{ Some(0).map(|x| x + 1) }`
+
+error: aborting due to 21 previous errors
 
index 81d903c15d32a29475cf338109586a22511ab306..f1d3252230bc2b7a23c57fa0037c7daa05df0040 100644 (file)
@@ -136,4 +136,19 @@ fn result_unwrap_or() {
     };
 }
 
+// don't lint in const fn
+const fn const_fn_option_unwrap_or() {
+    match Some(1) {
+        Some(s) => s,
+        None => 0,
+    };
+}
+
+const fn const_fn_result_unwrap_or() {
+    match Ok::<&str, &str>("Alice") {
+        Ok(s) => s,
+        Err(_) => "Bob",
+    };
+}
+
 fn main() {}
index 16105d379c3052ac8c709e0ea24925411d0fb237..c9eee25a5b1538d96a38e36d8922f44ea1bae6ff 100644 (file)
@@ -175,4 +175,19 @@ fn method(self) -> Option<i32> {
     };
 }
 
+// don't lint in const fn
+const fn const_fn_option_unwrap_or() {
+    match Some(1) {
+        Some(s) => s,
+        None => 0,
+    };
+}
+
+const fn const_fn_result_unwrap_or() {
+    match Ok::<&str, &str>("Alice") {
+        Ok(s) => s,
+        Err(_) => "Bob",
+    };
+}
+
 fn main() {}
diff --git a/tests/ui/match_single_binding2.fixed b/tests/ui/match_single_binding2.fixed
new file mode 100644 (file)
index 0000000..e73a85b
--- /dev/null
@@ -0,0 +1,37 @@
+// run-rustfix
+
+#![warn(clippy::match_single_binding)]
+#![allow(unused_variables)]
+
+fn main() {
+    // Lint (additional curly braces needed, see #6572)
+    struct AppendIter<I>
+    where
+        I: Iterator,
+    {
+        inner: Option<(I, <I as Iterator>::Item)>,
+    }
+
+    #[allow(dead_code)]
+    fn size_hint<I: Iterator>(iter: &AppendIter<I>) -> (usize, Option<usize>) {
+        match &iter.inner {
+            Some((iter, _item)) => {
+                let (min, max) = iter.size_hint();
+                (min.saturating_add(1), max.and_then(|max| max.checked_add(1)))
+            },
+            None => (0, Some(0)),
+        }
+    }
+
+    // Lint (no additional curly braces needed)
+    let opt = Some((5, 2));
+    let get_tup = || -> (i32, i32) { (1, 2) };
+    match opt {
+        #[rustfmt::skip]
+        Some((first, _second)) => {
+            let (a, b) = get_tup();
+            println!("a {:?} and b {:?}", a, b);
+        },
+        None => println!("nothing"),
+    }
+}
diff --git a/tests/ui/match_single_binding2.rs b/tests/ui/match_single_binding2.rs
new file mode 100644 (file)
index 0000000..7362cb3
--- /dev/null
@@ -0,0 +1,37 @@
+// run-rustfix
+
+#![warn(clippy::match_single_binding)]
+#![allow(unused_variables)]
+
+fn main() {
+    // Lint (additional curly braces needed, see #6572)
+    struct AppendIter<I>
+    where
+        I: Iterator,
+    {
+        inner: Option<(I, <I as Iterator>::Item)>,
+    }
+
+    #[allow(dead_code)]
+    fn size_hint<I: Iterator>(iter: &AppendIter<I>) -> (usize, Option<usize>) {
+        match &iter.inner {
+            Some((iter, _item)) => match iter.size_hint() {
+                (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))),
+            },
+            None => (0, Some(0)),
+        }
+    }
+
+    // Lint (no additional curly braces needed)
+    let opt = Some((5, 2));
+    let get_tup = || -> (i32, i32) { (1, 2) };
+    match opt {
+        #[rustfmt::skip]
+        Some((first, _second)) => {
+            match get_tup() {
+                (a, b) => println!("a {:?} and b {:?}", a, b),
+            }
+        },
+        None => println!("nothing"),
+    }
+}
diff --git a/tests/ui/match_single_binding2.stderr b/tests/ui/match_single_binding2.stderr
new file mode 100644 (file)
index 0000000..bc18d19
--- /dev/null
@@ -0,0 +1,34 @@
+error: this match could be written as a `let` statement
+  --> $DIR/match_single_binding2.rs:18:36
+   |
+LL |               Some((iter, _item)) => match iter.size_hint() {
+   |  ____________________________________^
+LL | |                 (min, max) => (min.saturating_add(1), max.and_then(|max| max.checked_add(1))),
+LL | |             },
+   | |_____________^
+   |
+   = note: `-D clippy::match-single-binding` implied by `-D warnings`
+help: consider using `let` statement
+   |
+LL |             Some((iter, _item)) => {
+LL |                 let (min, max) = iter.size_hint();
+LL |                 (min.saturating_add(1), max.and_then(|max| max.checked_add(1)))
+LL |             },
+   |
+
+error: this match could be written as a `let` statement
+  --> $DIR/match_single_binding2.rs:31:13
+   |
+LL | /             match get_tup() {
+LL | |                 (a, b) => println!("a {:?} and b {:?}", a, b),
+LL | |             }
+   | |_____________^
+   |
+help: consider using `let` statement
+   |
+LL |             let (a, b) = get_tup();
+LL |             println!("a {:?} and b {:?}", a, b);
+   |
+
+error: aborting due to 2 previous errors
+
index 519200977a798d4356e29bf8bea1b55dcce5fa16..d99f9af3faf5b73df4884fb0296c882683787502 100644 (file)
@@ -15,6 +15,16 @@ enum Color {
     Blue,
     Rgb(u8, u8, u8),
 }
+impl Color {
+    fn f(self) {
+        match self {
+            Self::Red => (),
+            Self::Green => (),
+            Self::Blue => (),
+            Self::Rgb(..) => (),
+        };
+    }
+}
 
 fn main() {
     let f = Foo::A;
@@ -56,4 +66,46 @@ fn main() {
         Color::Rgb(255, _, _) => {},
         _ => {},
     }
+
+    // References shouldn't change anything
+    match &color {
+        &Color::Red => (),
+        Color::Green => (),
+        &Color::Rgb(..) => (),
+        Color::Blue => (),
+    }
+
+    use self::Color as C;
+
+    match color {
+        C::Red => (),
+        C::Green => (),
+        C::Rgb(..) => (),
+        C::Blue => (),
+    }
+
+    match color {
+        C::Red => (),
+        Color::Green => (),
+        Color::Rgb(..) => (),
+        Color::Blue => (),
+    }
+
+    match Some(0) {
+        Some(0) => 0,
+        Some(_) => 1,
+        _ => 2,
+    };
+
+    #[non_exhaustive]
+    enum Bar {
+        A,
+        B,
+        C,
+    }
+    match Bar::A {
+        Bar::A => (),
+        Bar::B => (),
+        _ => (),
+    };
 }
index 1df917e085c7123a947294226dae591e207e0f3f..1752a95de4b2cca9ec06caa7cbe9558086187b5b 100644 (file)
@@ -15,6 +15,16 @@ enum Color {
     Blue,
     Rgb(u8, u8, u8),
 }
+impl Color {
+    fn f(self) {
+        match self {
+            Self::Red => (),
+            Self::Green => (),
+            Self::Blue => (),
+            _ => (),
+        };
+    }
+}
 
 fn main() {
     let f = Foo::A;
@@ -56,4 +66,46 @@ fn main() {
         Color::Rgb(255, _, _) => {},
         _ => {},
     }
+
+    // References shouldn't change anything
+    match &color {
+        &Color::Red => (),
+        Color::Green => (),
+        &Color::Rgb(..) => (),
+        &_ => (),
+    }
+
+    use self::Color as C;
+
+    match color {
+        C::Red => (),
+        C::Green => (),
+        C::Rgb(..) => (),
+        _ => (),
+    }
+
+    match color {
+        C::Red => (),
+        Color::Green => (),
+        Color::Rgb(..) => (),
+        _ => (),
+    }
+
+    match Some(0) {
+        Some(0) => 0,
+        Some(_) => 1,
+        _ => 2,
+    };
+
+    #[non_exhaustive]
+    enum Bar {
+        A,
+        B,
+        C,
+    }
+    match Bar::A {
+        Bar::A => (),
+        Bar::B => (),
+        _ => (),
+    };
 }
index 82790aa9e80bba14c4c5e652ac67654dc78f3554..34538dea8e5f437e836daa4e8aa4bf2dc8074ffd 100644 (file)
@@ -1,28 +1,52 @@
-error: wildcard match will miss any future added variants
-  --> $DIR/match_wildcard_for_single_variants.rs:24:9
+error: wildcard matches only a single variant and will also match any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:24:13
    |
-LL |         _ => {},
-   |         ^ help: try this: `Foo::C`
+LL |             _ => (),
+   |             ^ help: try this: `Self::Rgb(..)`
    |
    = note: `-D clippy::match-wildcard-for-single-variants` implied by `-D warnings`
 
-error: wildcard match will miss any future added variants
+error: wildcard matches only a single variant and will also match any future added variants
   --> $DIR/match_wildcard_for_single_variants.rs:34:9
    |
+LL |         _ => {},
+   |         ^ help: try this: `Foo::C`
+
+error: wildcard matches only a single variant and will also match any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:44:9
+   |
 LL |         _ => {},
    |         ^ help: try this: `Color::Blue`
 
-error: wildcard match will miss any future added variants
-  --> $DIR/match_wildcard_for_single_variants.rs:42:9
+error: wildcard matches only a single variant and will also match any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:52:9
    |
 LL |         _ => {},
    |         ^ help: try this: `Color::Blue`
 
-error: wildcard match will miss any future added variants
-  --> $DIR/match_wildcard_for_single_variants.rs:48:9
+error: wildcard matches only a single variant and will also match any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:58:9
    |
 LL |         _ => {},
    |         ^ help: try this: `Color::Blue`
 
-error: aborting due to 4 previous errors
+error: wildcard matches only a single variant and will also match any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:75:9
+   |
+LL |         &_ => (),
+   |         ^^ help: try this: `Color::Blue`
+
+error: wildcard matches only a single variant and will also match any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:84:9
+   |
+LL |         _ => (),
+   |         ^ help: try this: `C::Blue`
+
+error: wildcard matches only a single variant and will also match any future added variants
+  --> $DIR/match_wildcard_for_single_variants.rs:91:9
+   |
+LL |         _ => (),
+   |         ^ help: try this: `Color::Blue`
+
+error: aborting due to 8 previous errors
 
index 54e962e7116e82a36c531f74a231b97b03b97d74..3b6224254a0a72d6a1b868acbd695bf7bdd79075 100644 (file)
@@ -7,6 +7,7 @@
     clippy::mem_replace_with_default
 )]
 
+use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
 use std::mem;
 
 fn replace_option_with_none() {
@@ -19,9 +20,37 @@ fn replace_option_with_none() {
 fn replace_with_default() {
     let mut s = String::from("foo");
     let _ = std::mem::take(&mut s);
+
     let s = &mut String::from("foo");
     let _ = std::mem::take(s);
     let _ = std::mem::take(s);
+
+    let mut v = vec![123];
+    let _ = std::mem::take(&mut v);
+    let _ = std::mem::take(&mut v);
+    let _ = std::mem::take(&mut v);
+    let _ = std::mem::take(&mut v);
+
+    let mut hash_map: HashMap<i32, i32> = HashMap::new();
+    let _ = std::mem::take(&mut hash_map);
+
+    let mut btree_map: BTreeMap<i32, i32> = BTreeMap::new();
+    let _ = std::mem::take(&mut btree_map);
+
+    let mut vd: VecDeque<i32> = VecDeque::new();
+    let _ = std::mem::take(&mut vd);
+
+    let mut hash_set: HashSet<&str> = HashSet::new();
+    let _ = std::mem::take(&mut hash_set);
+
+    let mut btree_set: BTreeSet<&str> = BTreeSet::new();
+    let _ = std::mem::take(&mut btree_set);
+
+    let mut list: LinkedList<i32> = LinkedList::new();
+    let _ = std::mem::take(&mut list);
+
+    let mut binary_heap: BinaryHeap<i32> = BinaryHeap::new();
+    let _ = std::mem::take(&mut binary_heap);
 }
 
 fn main() {
index 60f527810716fb909e05271ef50ab43f9d93be60..0a36db9e92159ad6f0dadb1fa7fd57b92b47ae20 100644 (file)
@@ -7,6 +7,7 @@
     clippy::mem_replace_with_default
 )]
 
+use std::collections::{BTreeMap, BTreeSet, BinaryHeap, HashMap, HashSet, LinkedList, VecDeque};
 use std::mem;
 
 fn replace_option_with_none() {
@@ -19,9 +20,37 @@ fn replace_option_with_none() {
 fn replace_with_default() {
     let mut s = String::from("foo");
     let _ = std::mem::replace(&mut s, String::default());
+
     let s = &mut String::from("foo");
     let _ = std::mem::replace(s, String::default());
     let _ = std::mem::replace(s, Default::default());
+
+    let mut v = vec![123];
+    let _ = std::mem::replace(&mut v, Vec::default());
+    let _ = std::mem::replace(&mut v, Default::default());
+    let _ = std::mem::replace(&mut v, Vec::new());
+    let _ = std::mem::replace(&mut v, vec![]);
+
+    let mut hash_map: HashMap<i32, i32> = HashMap::new();
+    let _ = std::mem::replace(&mut hash_map, HashMap::new());
+
+    let mut btree_map: BTreeMap<i32, i32> = BTreeMap::new();
+    let _ = std::mem::replace(&mut btree_map, BTreeMap::new());
+
+    let mut vd: VecDeque<i32> = VecDeque::new();
+    let _ = std::mem::replace(&mut vd, VecDeque::new());
+
+    let mut hash_set: HashSet<&str> = HashSet::new();
+    let _ = std::mem::replace(&mut hash_set, HashSet::new());
+
+    let mut btree_set: BTreeSet<&str> = BTreeSet::new();
+    let _ = std::mem::replace(&mut btree_set, BTreeSet::new());
+
+    let mut list: LinkedList<i32> = LinkedList::new();
+    let _ = std::mem::replace(&mut list, LinkedList::new());
+
+    let mut binary_heap: BinaryHeap<i32> = BinaryHeap::new();
+    let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new());
 }
 
 fn main() {
index 245d33aa4f260141650f9f00401e6bbf49d5a974..f8aa1538bffa87769567e4790ea5d147caaf20e4 100644 (file)
@@ -1,5 +1,5 @@
 error: replacing an `Option` with `None`
-  --> $DIR/mem_replace.rs:14:13
+  --> $DIR/mem_replace.rs:15:13
    |
 LL |     let _ = mem::replace(&mut an_option, None);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
@@ -7,13 +7,13 @@ LL |     let _ = mem::replace(&mut an_option, None);
    = note: `-D clippy::mem-replace-option-with-none` implied by `-D warnings`
 
 error: replacing an `Option` with `None`
-  --> $DIR/mem_replace.rs:16:13
+  --> $DIR/mem_replace.rs:17:13
    |
 LL |     let _ = mem::replace(an_option, None);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Option::take()` instead: `an_option.take()`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:21:13
+  --> $DIR/mem_replace.rs:22:13
    |
 LL |     let _ = std::mem::replace(&mut s, String::default());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut s)`
@@ -21,16 +21,82 @@ LL |     let _ = std::mem::replace(&mut s, String::default());
    = note: `-D clippy::mem-replace-with-default` implied by `-D warnings`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:23:13
+  --> $DIR/mem_replace.rs:25:13
    |
 LL |     let _ = std::mem::replace(s, String::default());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
 
 error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
-  --> $DIR/mem_replace.rs:24:13
+  --> $DIR/mem_replace.rs:26:13
    |
 LL |     let _ = std::mem::replace(s, Default::default());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(s)`
 
-error: aborting due to 5 previous errors
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+  --> $DIR/mem_replace.rs:29:13
+   |
+LL |     let _ = std::mem::replace(&mut v, Vec::default());
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+  --> $DIR/mem_replace.rs:30:13
+   |
+LL |     let _ = std::mem::replace(&mut v, Default::default());
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+  --> $DIR/mem_replace.rs:31:13
+   |
+LL |     let _ = std::mem::replace(&mut v, Vec::new());
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+  --> $DIR/mem_replace.rs:32:13
+   |
+LL |     let _ = std::mem::replace(&mut v, vec![]);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut v)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+  --> $DIR/mem_replace.rs:35:13
+   |
+LL |     let _ = std::mem::replace(&mut hash_map, HashMap::new());
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_map)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+  --> $DIR/mem_replace.rs:38:13
+   |
+LL |     let _ = std::mem::replace(&mut btree_map, BTreeMap::new());
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_map)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+  --> $DIR/mem_replace.rs:41:13
+   |
+LL |     let _ = std::mem::replace(&mut vd, VecDeque::new());
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut vd)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+  --> $DIR/mem_replace.rs:44:13
+   |
+LL |     let _ = std::mem::replace(&mut hash_set, HashSet::new());
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut hash_set)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+  --> $DIR/mem_replace.rs:47:13
+   |
+LL |     let _ = std::mem::replace(&mut btree_set, BTreeSet::new());
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut btree_set)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+  --> $DIR/mem_replace.rs:50:13
+   |
+LL |     let _ = std::mem::replace(&mut list, LinkedList::new());
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut list)`
+
+error: replacing a value of type `T` with `T::default()` is better expressed using `std::mem::take`
+  --> $DIR/mem_replace.rs:53:13
+   |
+LL |     let _ = std::mem::replace(&mut binary_heap, BinaryHeap::new());
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::mem::take(&mut binary_heap)`
+
+error: aborting due to 16 previous errors
 
index 71fb3565224e44534757a84d4916c6212a73d9b9..fd8433870bb1b8e6bd07e4e45e4ef06b48d6f14f 100644 (file)
@@ -167,3 +167,28 @@ mod question_mark_both {
         needless_question_mark_result();
     }
 }
+
+// #6921 if a macro wraps an expr in Some(  ) and the ? is in the macro use,
+// the suggestion fails to apply; do not lint
+macro_rules! some_in_macro {
+    ($expr:expr) => {
+        || -> _ { Some($expr) }()
+    };
+}
+
+pub fn test1() {
+    let x = Some(3);
+    let _x = some_in_macro!(x?);
+}
+
+// this one is ok because both the ? and the Some are both inside the macro def
+macro_rules! some_and_qmark_in_macro {
+    ($expr:expr) => {
+        || -> Option<_> { Some($expr) }()
+    };
+}
+
+pub fn test2() {
+    let x = Some(3);
+    let _x = some_and_qmark_in_macro!(x?);
+}
index e31f6f48fa7c7dc9a474419a3b23a082deea2296..36d45ac7e03e29789c7a43cde1150c57ce2a928e 100644 (file)
@@ -167,3 +167,28 @@ fn main() {
         needless_question_mark_result();
     }
 }
+
+// #6921 if a macro wraps an expr in Some(  ) and the ? is in the macro use,
+// the suggestion fails to apply; do not lint
+macro_rules! some_in_macro {
+    ($expr:expr) => {
+        || -> _ { Some($expr) }()
+    };
+}
+
+pub fn test1() {
+    let x = Some(3);
+    let _x = some_in_macro!(x?);
+}
+
+// this one is ok because both the ? and the Some are both inside the macro def
+macro_rules! some_and_qmark_in_macro {
+    ($expr:expr) => {
+        || -> Option<_> { Some(Some($expr)?) }()
+    };
+}
+
+pub fn test2() {
+    let x = Some(3);
+    let _x = some_and_qmark_in_macro!(x?);
+}
index 983c56031d8f5301879a48ce16497a35ce065c5d..7cbf1e505adf63dff27f884fb4315c971bb1a1a3 100644 (file)
@@ -84,5 +84,16 @@ error: question mark operator is useless here
 LL |         Ok(to.magic?) // should be triggered
    |         ^^^^^^^^^^^^^ help: try: `to.magic`
 
-error: aborting due to 14 previous errors
+error: question mark operator is useless here
+  --> $DIR/needless_question_mark.rs:187:27
+   |
+LL |         || -> Option<_> { Some(Some($expr)?) }()
+   |                           ^^^^^^^^^^^^^^^^^^ help: try: `Some($expr)`
+...
+LL |     let _x = some_and_qmark_in_macro!(x?);
+   |              ---------------------------- in this macro invocation
+   |
+   = note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 15 previous errors
 
index e82873629a54b0de91ad1068fff7d552001fe81b..2f315ffe2983ebebc506b2a5eb33bf7d2f87647b 100644 (file)
@@ -340,3 +340,13 @@ fn new() -> Option<(Self, u32)>
         }
     }
 }
+
+// issue #1724
+struct RetOtherSelf<T>(T);
+struct RetOtherSelfWrapper<T>(T);
+
+impl RetOtherSelf<T> {
+    fn new(t: T) -> RetOtherSelf<RetOtherSelfWrapper<T>> {
+        RetOtherSelf(RetOtherSelfWrapper(t))
+    }
+}
index 3b6041823d8786672ec6bb3d7bdae19acaeaa428..64659b63f469934e3f0aecc5e8359fbaeb557b78 100644 (file)
@@ -159,4 +159,19 @@ pub fn new() -> Self {
     }
 }
 
+// see #6933
+pub struct FooGenerics<T>(std::marker::PhantomData<T>);
+impl<T> FooGenerics<T> {
+    pub fn new() -> Self {
+        Self(Default::default())
+    }
+}
+
+pub struct BarGenerics<T>(std::marker::PhantomData<T>);
+impl<T: Copy> BarGenerics<T> {
+    pub fn new() -> Self {
+        Self(Default::default())
+    }
+}
+
 fn main() {}
index e529e441eb735c6b60cb59d5090fa5a22763bfdb..973836f75a905d02165931646bef411e422ff52b 100644 (file)
@@ -43,7 +43,7 @@ LL | |     }
    |
 help: try this
    |
-LL | impl Default for LtKo<'c> {
+LL | impl<'c> Default for LtKo<'c> {
 LL |     fn default() -> Self {
 LL |         Self::new()
 LL |     }
@@ -67,5 +67,39 @@ LL |     }
 LL | }
    |
 
-error: aborting due to 4 previous errors
+error: you should consider adding a `Default` implementation for `FooGenerics<T>`
+  --> $DIR/new_without_default.rs:165:5
+   |
+LL | /     pub fn new() -> Self {
+LL | |         Self(Default::default())
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL | impl<T> Default for FooGenerics<T> {
+LL |     fn default() -> Self {
+LL |         Self::new()
+LL |     }
+LL | }
+   |
+
+error: you should consider adding a `Default` implementation for `BarGenerics<T>`
+  --> $DIR/new_without_default.rs:172:5
+   |
+LL | /     pub fn new() -> Self {
+LL | |         Self(Default::default())
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL | impl<T: Copy> Default for BarGenerics<T> {
+LL |     fn default() -> Self {
+LL |         Self::new()
+LL |     }
+LL | }
+   |
+
+error: aborting due to 6 previous errors
 
index 64347cae5da385f2888cb9287f36b95f8746bd98..4390ff7dc30443ad89e792acca983a79b5deb211 100644 (file)
@@ -120,6 +120,9 @@ fn test_or_with_ctors() {
 
     let slice = &["foo"][..];
     let _ = opt.ok_or(slice.len());
+
+    let string = "foo";
+    let _ = opt.ok_or(string.len());
 }
 
 // Issue 4514 - early return
@@ -132,4 +135,18 @@ fn f() -> Option<()> {
     Some(())
 }
 
+mod issue6675 {
+    unsafe fn foo() {
+        let mut s = "test".to_owned();
+        None.unwrap_or_else(|| s.as_mut_vec());
+    }
+
+    fn bar() {
+        let mut s = "test".to_owned();
+        None.unwrap_or_else(|| unsafe { s.as_mut_vec() });
+        #[rustfmt::skip]
+        None.unwrap_or_else(|| unsafe { s.as_mut_vec() });
+    }
+}
+
 fn main() {}
index 7faab0017b2e8f7485c8f07d2a82575c9453ca2d..75908c974cc9dee899caba732b0cb72cd118e982 100644 (file)
@@ -120,6 +120,9 @@ fn test_or_with_ctors() {
 
     let slice = &["foo"][..];
     let _ = opt.ok_or(slice.len());
+
+    let string = "foo";
+    let _ = opt.ok_or(string.len());
 }
 
 // Issue 4514 - early return
@@ -132,4 +135,18 @@ fn f() -> Option<()> {
     Some(())
 }
 
+mod issue6675 {
+    unsafe fn foo() {
+        let mut s = "test".to_owned();
+        None.unwrap_or(s.as_mut_vec());
+    }
+
+    fn bar() {
+        let mut s = "test".to_owned();
+        None.unwrap_or(unsafe { s.as_mut_vec() });
+        #[rustfmt::skip]
+        None.unwrap_or( unsafe { s.as_mut_vec() }    );
+    }
+}
+
 fn main() {}
index 1e2bfd490e099b942a2a0687ccf08f5eb6d8aeef..9905029ce91fa9f56bf95c356019af250e31576d 100644 (file)
@@ -114,5 +114,23 @@ error: use of `or` followed by a function call
 LL |         .or(Some(Bar(b, Duration::from_secs(2))));
    |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `or_else(|| Some(Bar(b, Duration::from_secs(2))))`
 
-error: aborting due to 19 previous errors
+error: use of `unwrap_or` followed by a function call
+  --> $DIR/or_fun_call.rs:141:14
+   |
+LL |         None.unwrap_or(s.as_mut_vec());
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| s.as_mut_vec())`
+
+error: use of `unwrap_or` followed by a function call
+  --> $DIR/or_fun_call.rs:146:14
+   |
+LL |         None.unwrap_or(unsafe { s.as_mut_vec() });
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })`
+
+error: use of `unwrap_or` followed by a function call
+  --> $DIR/or_fun_call.rs:148:14
+   |
+LL |         None.unwrap_or( unsafe { s.as_mut_vec() }    );
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try this: `unwrap_or_else(|| unsafe { s.as_mut_vec() })`
+
+error: aborting due to 22 previous errors
 
index 208d95326285133812d440de7d0740216a34ee6e..1754c418381a13c4b25efaae6a02fa2532e1a7da 100644 (file)
@@ -1,8 +1,8 @@
 error: use of `Debug`-based formatting
-  --> $DIR/print.rs:11:19
+  --> $DIR/print.rs:11:20
    |
 LL |         write!(f, "{:?}", 43.1415)
-   |                   ^^^^^^
+   |                    ^^^^
    |
    = note: `-D clippy::use-debug` implied by `-D warnings`
 
@@ -33,10 +33,10 @@ LL |     print!("Hello {:?}", "World");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: use of `Debug`-based formatting
-  --> $DIR/print.rs:28:12
+  --> $DIR/print.rs:28:19
    |
 LL |     print!("Hello {:?}", "World");
-   |            ^^^^^^^^^^^^
+   |                   ^^^^
 
 error: use of `print!`
   --> $DIR/print.rs:30:5
@@ -45,10 +45,10 @@ LL |     print!("Hello {:#?}", "#orld");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: use of `Debug`-based formatting
-  --> $DIR/print.rs:30:12
+  --> $DIR/print.rs:30:19
    |
 LL |     print!("Hello {:#?}", "#orld");
-   |            ^^^^^^^^^^^^^
+   |                   ^^^^^
 
 error: aborting due to 8 previous errors
 
index e284aece236faa4462e4b288ead9799735528d6e..54a4084c89e116f1e532c9ac76c8e5bd63d11f47 100644 (file)
@@ -5,66 +5,120 @@ LL |     print!("Hello {}", "world");
    |                        ^^^^^^^
    |
    = note: `-D clippy::print-literal` implied by `-D warnings`
+help: try this
+   |
+LL |     print!("Hello world");
+   |                   ^^^^^--
 
 error: literal with an empty format string
   --> $DIR/print_literal.rs:26:36
    |
 LL |     println!("Hello {} {}", world, "world");
    |                                    ^^^^^^^
+   |
+help: try this
+   |
+LL |     println!("Hello {} world", world);
+   |                        ^^^^^       --
 
 error: literal with an empty format string
   --> $DIR/print_literal.rs:27:26
    |
 LL |     println!("Hello {}", "world");
    |                          ^^^^^^^
+   |
+help: try this
+   |
+LL |     println!("Hello world");
+   |                     ^^^^^--
 
 error: literal with an empty format string
   --> $DIR/print_literal.rs:32:25
    |
 LL |     println!("{0} {1}", "hello", "world");
    |                         ^^^^^^^
+   |
+help: try this
+   |
+LL |     println!("hello {1}", "world");
+   |               ^^^^^    --
 
 error: literal with an empty format string
   --> $DIR/print_literal.rs:32:34
    |
 LL |     println!("{0} {1}", "hello", "world");
    |                                  ^^^^^^^
+   |
+help: try this
+   |
+LL |     println!("{0} world", "hello");
+   |                   ^^^^^         --
 
 error: literal with an empty format string
   --> $DIR/print_literal.rs:33:25
    |
 LL |     println!("{1} {0}", "hello", "world");
    |                         ^^^^^^^
+   |
+help: try this
+   |
+LL |     println!("{1} hello", "world");
+   |                   ^^^^^--
 
 error: literal with an empty format string
   --> $DIR/print_literal.rs:33:34
    |
 LL |     println!("{1} {0}", "hello", "world");
    |                                  ^^^^^^^
+   |
+help: try this
+   |
+LL |     println!("world {0}", "hello");
+   |               ^^^^^             --
 
 error: literal with an empty format string
-  --> $DIR/print_literal.rs:36:35
+  --> $DIR/print_literal.rs:36:29
    |
 LL |     println!("{foo} {bar}", foo = "hello", bar = "world");
-   |                                   ^^^^^^^
+   |                             ^^^^^^^^^^^^^
+   |
+help: try this
+   |
+LL |     println!("hello {bar}", bar = "world");
+   |               ^^^^^      --
 
 error: literal with an empty format string
-  --> $DIR/print_literal.rs:36:50
+  --> $DIR/print_literal.rs:36:44
    |
 LL |     println!("{foo} {bar}", foo = "hello", bar = "world");
-   |                                                  ^^^^^^^
+   |                                            ^^^^^^^^^^^^^
+   |
+help: try this
+   |
+LL |     println!("{foo} world", foo = "hello");
+   |                     ^^^^^               --
 
 error: literal with an empty format string
-  --> $DIR/print_literal.rs:37:35
+  --> $DIR/print_literal.rs:37:29
    |
 LL |     println!("{bar} {foo}", foo = "hello", bar = "world");
-   |                                   ^^^^^^^
+   |                             ^^^^^^^^^^^^^
+   |
+help: try this
+   |
+LL |     println!("{bar} hello", bar = "world");
+   |                     ^^^^^--
 
 error: literal with an empty format string
-  --> $DIR/print_literal.rs:37:50
+  --> $DIR/print_literal.rs:37:44
    |
 LL |     println!("{bar} {foo}", foo = "hello", bar = "world");
-   |                                                  ^^^^^^^
+   |                                            ^^^^^^^^^^^^^
+   |
+help: try this
+   |
+LL |     println!("world {foo}", foo = "hello");
+   |               ^^^^^                     --
 
 error: aborting due to 11 previous errors
 
index f0dc3b3d06bbc7f4f30d4c32e51e0d9dd2a968af..72bc6ef35d317b02f9c5300affc0e6923fdd7790 100644 (file)
@@ -1,8 +1,9 @@
 // aux-build:option_helpers.rs
+#![warn(clippy::search_is_some)]
+#![allow(dead_code)]
 extern crate option_helpers;
 use option_helpers::IteratorFalsePositives;
 
-#[warn(clippy::search_is_some)]
 #[rustfmt::skip]
 fn main() {
     let v = vec![3, 2, 1, 0, -1, -2, -3];
@@ -36,3 +37,37 @@ fn main() {
     // `Pattern` that is not a string
     let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_some();
 }
+
+#[rustfmt::skip]
+fn is_none() {
+    let v = vec![3, 2, 1, 0, -1, -2, -3];
+    let y = &&42;
+
+
+    // Check `find().is_none()`, multi-line case.
+    let _ = v.iter().find(|&x| {
+                              *x < 0
+                          }
+                   ).is_none();
+
+    // Check `position().is_none()`, multi-line case.
+    let _ = v.iter().position(|&x| {
+                                  x < 0
+                              }
+                   ).is_none();
+
+    // Check `rposition().is_none()`, multi-line case.
+    let _ = v.iter().rposition(|&x| {
+                                   x < 0
+                               }
+                   ).is_none();
+
+    // Check that we don't lint if the caller is not an `Iterator` or string
+    let falsepos = IteratorFalsePositives { foo: 0 };
+    let _ = falsepos.find().is_none();
+    let _ = falsepos.position().is_none();
+    let _ = falsepos.rposition().is_none();
+    // check that we don't lint if `find()` is called with
+    // `Pattern` that is not a string
+    let _ = "hello world".find(|c: char| c == 'o' || c == 'l').is_none();
+}
index c601f568c609bccf4cf3d7b9c60ac5f3823da45a..f3c758e451ef1436047b24be7d696156b4c075fb 100644 (file)
@@ -1,5 +1,5 @@
 error: called `is_some()` after searching an `Iterator` with `find`
-  --> $DIR/search_is_some.rs:13:13
+  --> $DIR/search_is_some.rs:14:13
    |
 LL |       let _ = v.iter().find(|&x| {
    |  _____________^
@@ -12,7 +12,7 @@ LL | |                    ).is_some();
    = help: this is more succinctly expressed by calling `any()`
 
 error: called `is_some()` after searching an `Iterator` with `position`
-  --> $DIR/search_is_some.rs:19:13
+  --> $DIR/search_is_some.rs:20:13
    |
 LL |       let _ = v.iter().position(|&x| {
    |  _____________^
@@ -24,7 +24,7 @@ LL | |                    ).is_some();
    = help: this is more succinctly expressed by calling `any()`
 
 error: called `is_some()` after searching an `Iterator` with `rposition`
-  --> $DIR/search_is_some.rs:25:13
+  --> $DIR/search_is_some.rs:26:13
    |
 LL |       let _ = v.iter().rposition(|&x| {
    |  _____________^
@@ -35,5 +35,41 @@ LL | |                    ).is_some();
    |
    = help: this is more succinctly expressed by calling `any()`
 
-error: aborting due to 3 previous errors
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some.rs:48:13
+   |
+LL |       let _ = v.iter().find(|&x| {
+   |  _____________^
+LL | |                               *x < 0
+LL | |                           }
+LL | |                    ).is_none();
+   | |______________________________^
+   |
+   = help: this is more succinctly expressed by calling `any()` with negation
+
+error: called `is_none()` after searching an `Iterator` with `position`
+  --> $DIR/search_is_some.rs:54:13
+   |
+LL |       let _ = v.iter().position(|&x| {
+   |  _____________^
+LL | |                                   x < 0
+LL | |                               }
+LL | |                    ).is_none();
+   | |______________________________^
+   |
+   = help: this is more succinctly expressed by calling `any()` with negation
+
+error: called `is_none()` after searching an `Iterator` with `rposition`
+  --> $DIR/search_is_some.rs:60:13
+   |
+LL |       let _ = v.iter().rposition(|&x| {
+   |  _____________^
+LL | |                                    x < 0
+LL | |                                }
+LL | |                    ).is_none();
+   | |______________________________^
+   |
+   = help: this is more succinctly expressed by calling `any()` with negation
+
+error: aborting due to 6 previous errors
 
index dc3f290e56246d14890ae48db869f6de311c4b1b..62ff16f67f41b7aa5641af3829a70ec3c04be4fd 100644 (file)
@@ -1,5 +1,5 @@
 // run-rustfix
-
+#![allow(dead_code)]
 #![warn(clippy::search_is_some)]
 
 fn main() {
@@ -33,3 +33,36 @@ fn main() {
     let _ = s1[2..].contains(&s2);
     let _ = s1[2..].contains(&s2[2..]);
 }
+
+fn is_none() {
+    let v = vec![3, 2, 1, 0, -1, -2, -3];
+    let y = &&42;
+
+    // Check `find().is_none()`, single-line case.
+    let _ = !v.iter().any(|x| *x < 0);
+    let _ = !(0..1).any(|x| **y == x); // one dereference less
+    let _ = !(0..1).any(|x| x == 0);
+    let _ = !v.iter().any(|x| *x == 0);
+
+    // Check `position().is_none()`, single-line case.
+    let _ = !v.iter().any(|&x| x < 0);
+
+    // Check `rposition().is_none()`, single-line case.
+    let _ = !v.iter().any(|&x| x < 0);
+
+    let s1 = String::from("hello world");
+    let s2 = String::from("world");
+
+    // caller of `find()` is a `&`static str`
+    let _ = !"hello world".contains("world");
+    let _ = !"hello world".contains(&s2);
+    let _ = !"hello world".contains(&s2[2..]);
+    // caller of `find()` is a `String`
+    let _ = !s1.contains("world");
+    let _ = !s1.contains(&s2);
+    let _ = !s1.contains(&s2[2..]);
+    // caller of `find()` is slice of `String`
+    let _ = !s1[2..].contains("world");
+    let _ = !s1[2..].contains(&s2);
+    let _ = !s1[2..].contains(&s2[2..]);
+}
index 146cf5adf1b0f97fc0e7d6536416c0192d06733f..8407f71664740d1aa8a4f6daf33e4841bec1bba3 100644 (file)
@@ -1,5 +1,5 @@
 // run-rustfix
-
+#![allow(dead_code)]
 #![warn(clippy::search_is_some)]
 
 fn main() {
@@ -33,3 +33,36 @@ fn main() {
     let _ = s1[2..].find(&s2).is_some();
     let _ = s1[2..].find(&s2[2..]).is_some();
 }
+
+fn is_none() {
+    let v = vec![3, 2, 1, 0, -1, -2, -3];
+    let y = &&42;
+
+    // Check `find().is_none()`, single-line case.
+    let _ = v.iter().find(|&x| *x < 0).is_none();
+    let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
+    let _ = (0..1).find(|x| *x == 0).is_none();
+    let _ = v.iter().find(|x| **x == 0).is_none();
+
+    // Check `position().is_none()`, single-line case.
+    let _ = v.iter().position(|&x| x < 0).is_none();
+
+    // Check `rposition().is_none()`, single-line case.
+    let _ = v.iter().rposition(|&x| x < 0).is_none();
+
+    let s1 = String::from("hello world");
+    let s2 = String::from("world");
+
+    // caller of `find()` is a `&`static str`
+    let _ = "hello world".find("world").is_none();
+    let _ = "hello world".find(&s2).is_none();
+    let _ = "hello world".find(&s2[2..]).is_none();
+    // caller of `find()` is a `String`
+    let _ = s1.find("world").is_none();
+    let _ = s1.find(&s2).is_none();
+    let _ = s1.find(&s2[2..]).is_none();
+    // caller of `find()` is slice of `String`
+    let _ = s1[2..].find("world").is_none();
+    let _ = s1[2..].find(&s2).is_none();
+    let _ = s1[2..].find(&s2[2..]).is_none();
+}
index 23c1d9a901b97da68cdbd6636f55a73c99ff069e..bd1b6955a97283bba14a32d8e9bad58b7c638493 100644 (file)
@@ -90,5 +90,95 @@ error: called `is_some()` after calling `find()` on a string
 LL |     let _ = s1[2..].find(&s2[2..]).is_some();
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: use `contains()` instead: `contains(&s2[2..])`
 
-error: aborting due to 15 previous errors
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable.rs:42:13
+   |
+LL |     let _ = v.iter().find(|&x| *x < 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x < 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable.rs:43:13
+   |
+LL |     let _ = (0..1).find(|x| **y == *x).is_none(); // one dereference less
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| **y == x)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable.rs:44:13
+   |
+LL |     let _ = (0..1).find(|x| *x == 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!(0..1).any(|x| x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `find`
+  --> $DIR/search_is_some_fixable.rs:45:13
+   |
+LL |     let _ = v.iter().find(|x| **x == 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|x| *x == 0)`
+
+error: called `is_none()` after searching an `Iterator` with `position`
+  --> $DIR/search_is_some_fixable.rs:48:13
+   |
+LL |     let _ = v.iter().position(|&x| x < 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
+
+error: called `is_none()` after searching an `Iterator` with `rposition`
+  --> $DIR/search_is_some_fixable.rs:51:13
+   |
+LL |     let _ = v.iter().rposition(|&x| x < 0).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.any()` instead: `!v.iter().any(|&x| x < 0)`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable.rs:57:13
+   |
+LL |     let _ = "hello world".find("world").is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable.rs:58:13
+   |
+LL |     let _ = "hello world".find(&s2).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable.rs:59:13
+   |
+LL |     let _ = "hello world".find(&s2[2..]).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!"hello world".contains(&s2[2..])`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable.rs:61:13
+   |
+LL |     let _ = s1.find("world").is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable.rs:62:13
+   |
+LL |     let _ = s1.find(&s2).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable.rs:63:13
+   |
+LL |     let _ = s1.find(&s2[2..]).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1.contains(&s2[2..])`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable.rs:65:13
+   |
+LL |     let _ = s1[2..].find("world").is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains("world")`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable.rs:66:13
+   |
+LL |     let _ = s1[2..].find(&s2).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2)`
+
+error: called `is_none()` after calling `find()` on a string
+  --> $DIR/search_is_some_fixable.rs:67:13
+   |
+LL |     let _ = s1[2..].find(&s2[2..]).is_none();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `!_.contains()` instead: `!s1[2..].contains(&s2[2..])`
+
+error: aborting due to 30 previous errors
 
index ccf8f61c4a92c7528944a3c1cf6066b9e1c969d3..dd22bfa5c53ef43e847237bbfc8abc554259b0ad 100644 (file)
@@ -8,10 +8,16 @@ fn str_lit_as_bytes() {
 
     let bs = br###"raw string with 3# plus " ""###;
 
+    let bs = b"lit to string".to_vec();
+    let bs = b"lit to owned".to_vec();
+
     // no warning, because these cannot be written as byte string literals:
     let ubs = "☃".as_bytes();
     let ubs = "hello there! this is a very long string".as_bytes();
 
+    let ubs = "☃".to_string().into_bytes();
+    let ubs = "this is also too long and shouldn't be fixed".to_string().into_bytes();
+
     let strify = stringify!(foobar).as_bytes();
 
     let current_version = env!("CARGO_PKG_VERSION").as_bytes();
index 178df08e249ef3c2e20c9d1fc7e2267115b5fa85..d2a710ed6b8ca5f575ac836d3ec80f5d563dbf1d 100644 (file)
@@ -8,10 +8,16 @@ fn str_lit_as_bytes() {
 
     let bs = r###"raw string with 3# plus " ""###.as_bytes();
 
+    let bs = "lit to string".to_string().into_bytes();
+    let bs = "lit to owned".to_owned().into_bytes();
+
     // no warning, because these cannot be written as byte string literals:
     let ubs = "☃".as_bytes();
     let ubs = "hello there! this is a very long string".as_bytes();
 
+    let ubs = "☃".to_string().into_bytes();
+    let ubs = "this is also too long and shouldn't be fixed".to_string().into_bytes();
+
     let strify = stringify!(foobar).as_bytes();
 
     let current_version = env!("CARGO_PKG_VERSION").as_bytes();
index 99c512354d589f722d47003325412ee1b6432bb1..e0ddb070b504456eb8e6f237c9f30e0c5e273d5e 100644 (file)
@@ -12,17 +12,29 @@ error: calling `as_bytes()` on a string literal
 LL |     let bs = r###"raw string with 3# plus " ""###.as_bytes();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `br###"raw string with 3# plus " ""###`
 
+error: calling `into_bytes()` on a string literal
+  --> $DIR/string_lit_as_bytes.rs:11:14
+   |
+LL |     let bs = "lit to string".to_string().into_bytes();
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"lit to string".to_vec()`
+
+error: calling `into_bytes()` on a string literal
+  --> $DIR/string_lit_as_bytes.rs:12:14
+   |
+LL |     let bs = "lit to owned".to_owned().into_bytes();
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"lit to owned".to_vec()`
+
 error: calling `as_bytes()` on `include_str!(..)`
-  --> $DIR/string_lit_as_bytes.rs:19:22
+  --> $DIR/string_lit_as_bytes.rs:25:22
    |
 LL |     let includestr = include_str!("entry_unfixable.rs").as_bytes();
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `include_bytes!(..)` instead: `include_bytes!("entry_unfixable.rs")`
 
 error: calling `as_bytes()` on a string literal
-  --> $DIR/string_lit_as_bytes.rs:21:13
+  --> $DIR/string_lit_as_bytes.rs:27:13
    |
 LL |     let _ = "string with newline/t/n".as_bytes();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using a byte string literal instead: `b"string with newline/t/n"`
 
-error: aborting due to 4 previous errors
+error: aborting due to 6 previous errors
 
index d838d8fde2105244a90d0067fb5386172686dbaa..3a2a10cf09ea51082cacd662a474d28ef8cdd252 100644 (file)
@@ -2,4 +2,31 @@
 
 fn main() {
     let _ = (0..3).map(|x| x + 2).count();
+
+    let f = |x| x + 1;
+    let _ = (0..3).map(f).count();
+}
+
+fn negative() {
+    // closure with side effects
+    let mut sum = 0;
+    let _ = (0..3).map(|x| sum += x).count();
+
+    // closure variable with side effects
+    let ext_closure = |x| sum += x;
+    let _ = (0..3).map(ext_closure).count();
+
+    // closure that returns unit
+    let _ = (0..3)
+        .map(|x| {
+            // do nothing
+        })
+        .count();
+
+    // external function
+    let _ = (0..3).map(do_something).count();
+}
+
+fn do_something<T>(t: T) -> String {
+    unimplemented!()
 }
index e1b4ba40376f88d0904b96cf6c459e57bbbd56ee..8c3f36584a5bd1bcee836fdeeef230d59c140d04 100644 (file)
@@ -7,5 +7,13 @@ LL |     let _ = (0..3).map(|x| x + 2).count();
    = note: `-D clippy::suspicious-map` implied by `-D warnings`
    = help: make sure you did not confuse `map` with `filter` or `for_each`
 
-error: aborting due to previous error
+error: this call to `map()` won't have an effect on the call to `count()`
+  --> $DIR/suspicious_map.rs:7:13
+   |
+LL |     let _ = (0..3).map(f).count();
+   |             ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: make sure you did not confuse `map` with `filter` or `for_each`
+
+error: aborting due to 2 previous errors
 
index 8981d13e8eabb61c1e2888f237af5016c8e9ccbe..70aa448af68ee4774dc0a0d4762f6e20bb538baa 100644 (file)
@@ -1,22 +1,22 @@
-error: casting to the same type is unnecessary (`i32` -> `i32`)
+error: casting integer literal to `i32` is unnecessary
   --> $DIR/unnecessary_cast.rs:6:5
    |
 LL |     1i32 as i32;
-   |     ^^^^^^^^^^^
+   |     ^^^^^^^^^^^ help: try: `1_i32`
    |
    = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
 
-error: casting to the same type is unnecessary (`f32` -> `f32`)
+error: casting float literal to `f32` is unnecessary
   --> $DIR/unnecessary_cast.rs:7:5
    |
 LL |     1f32 as f32;
-   |     ^^^^^^^^^^^
+   |     ^^^^^^^^^^^ help: try: `1_f32`
 
 error: casting to the same type is unnecessary (`bool` -> `bool`)
   --> $DIR/unnecessary_cast.rs:8:5
    |
 LL |     false as bool;
-   |     ^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^ help: try: `false`
 
 error: aborting due to 3 previous errors
 
index 735909887acb189ac97b01a63708ae738989a2b1..8c09c6f5b23d273692a9c5d10bd16fb419e1f60c 100644 (file)
@@ -20,4 +20,8 @@ enum Flags {
 // `GccLlvmSomething`
 struct GCCLLVMSomething;
 
+// public items must not be linted
+pub struct NOWARNINGHERE;
+pub struct ALSONoWarningHERE;
+
 fn main() {}
index b94d5448d9238f7dd1cda66d2be02f4d4d0bfe5f..1282befdfb36b9e3b9759583672492960b453708 100644 (file)
@@ -75,13 +75,13 @@ mod lifetimes {
 
 mod issue2894 {
     trait IntoBytes {
-        fn to_bytes(&self) -> Vec<u8>;
+        fn to_bytes(self) -> Vec<u8>;
     }
 
     // This should not be linted
     impl IntoBytes for u8 {
-        fn to_bytes(&self) -> Vec<u8> {
-            vec![*self]
+        fn to_bytes(self) -> Vec<u8> {
+            vec![self]
         }
     }
 }
index ac99c6d9d7bb1aa97bb8bdde293042fe8db4f349..7aaac7b2414e6fa1bf906f905d4b6e49dd645014 100644 (file)
@@ -75,13 +75,13 @@ fn clone(&self) -> Foo<'a> {
 
 mod issue2894 {
     trait IntoBytes {
-        fn to_bytes(&self) -> Vec<u8>;
+        fn to_bytes(self) -> Vec<u8>;
     }
 
     // This should not be linted
     impl IntoBytes for u8 {
-        fn to_bytes(&self) -> Vec<u8> {
-            vec![*self]
+        fn to_bytes(self) -> Vec<u8> {
+            vec![self]
         }
     }
 }
index c266f684a36f94f042c11e9491034efa40296446..fd754e4c794f63416a1a496649eec7ff7e5d4126 100644 (file)
@@ -77,7 +77,7 @@ fn main() {
     let error_kind = ErrorKind::NotFound;
     match error_kind {
         ErrorKind::NotFound => {},
-        std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _ => {},
+        ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | _ => {},
     }
     match error_kind {
         ErrorKind::NotFound => {},
index 0da2b68ba0b2f80fc6c3885bc6d4b6b5f14abd23..a513a62c748d6a7a683e70c9721ff93289f6b59c 100644 (file)
@@ -1,4 +1,4 @@
-error: wildcard match will miss any future added variants
+error: wildcard match will also match any future added variants
   --> $DIR/wildcard_enum_match_arm.rs:39:9
    |
 LL |         _ => eprintln!("Not red"),
@@ -10,29 +10,29 @@ note: the lint level is defined here
 LL | #![deny(clippy::wildcard_enum_match_arm)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: wildcard match will miss any future added variants
+error: wildcard match will also match any future added variants
   --> $DIR/wildcard_enum_match_arm.rs:43:9
    |
 LL |         _not_red => eprintln!("Not red"),
    |         ^^^^^^^^ help: try this: `_not_red @ Color::Green | _not_red @ Color::Blue | _not_red @ Color::Rgb(..) | _not_red @ Color::Cyan`
 
-error: wildcard match will miss any future added variants
+error: wildcard match will also match any future added variants
   --> $DIR/wildcard_enum_match_arm.rs:47:9
    |
 LL |         not_red => format!("{:?}", not_red),
    |         ^^^^^^^ help: try this: `not_red @ Color::Green | not_red @ Color::Blue | not_red @ Color::Rgb(..) | not_red @ Color::Cyan`
 
-error: wildcard match will miss any future added variants
+error: wildcard match will also match any future added variants
   --> $DIR/wildcard_enum_match_arm.rs:63:9
    |
 LL |         _ => "No red",
    |         ^ help: try this: `Color::Red | Color::Green | Color::Blue | Color::Rgb(..) | Color::Cyan`
 
-error: match on non-exhaustive enum doesn't explicitly match all known variants
+error: wildcard matches known variants and will also match future added variants
   --> $DIR/wildcard_enum_match_arm.rs:80:9
    |
 LL |         _ => {},
-   |         ^ help: try this: `std::io::ErrorKind::PermissionDenied | std::io::ErrorKind::ConnectionRefused | std::io::ErrorKind::ConnectionReset | std::io::ErrorKind::ConnectionAborted | std::io::ErrorKind::NotConnected | std::io::ErrorKind::AddrInUse | std::io::ErrorKind::AddrNotAvailable | std::io::ErrorKind::BrokenPipe | std::io::ErrorKind::AlreadyExists | std::io::ErrorKind::WouldBlock | std::io::ErrorKind::InvalidInput | std::io::ErrorKind::InvalidData | std::io::ErrorKind::TimedOut | std::io::ErrorKind::WriteZero | std::io::ErrorKind::Interrupted | std::io::ErrorKind::Other | std::io::ErrorKind::UnexpectedEof | _`
+   |         ^ help: try this: `ErrorKind::PermissionDenied | ErrorKind::ConnectionRefused | ErrorKind::ConnectionReset | ErrorKind::ConnectionAborted | ErrorKind::NotConnected | ErrorKind::AddrInUse | ErrorKind::AddrNotAvailable | ErrorKind::BrokenPipe | ErrorKind::AlreadyExists | ErrorKind::WouldBlock | ErrorKind::InvalidInput | ErrorKind::InvalidData | ErrorKind::TimedOut | ErrorKind::WriteZero | ErrorKind::Interrupted | ErrorKind::Other | ErrorKind::UnexpectedEof | _`
 
 error: aborting due to 5 previous errors
 
index e54d89ecf29e67ded79b647991050b226b7d4234..507a78e82805030cf9ae9b34be7b0228c57b3f27 100644 (file)
@@ -5,66 +5,120 @@ LL |     write!(&mut v, "Hello {}", "world");
    |                                ^^^^^^^
    |
    = note: `-D clippy::write-literal` implied by `-D warnings`
+help: try this
+   |
+LL |     write!(&mut v, "Hello world");
+   |                           ^^^^^--
 
 error: literal with an empty format string
   --> $DIR/write_literal.rs:31:44
    |
 LL |     writeln!(&mut v, "Hello {} {}", world, "world");
    |                                            ^^^^^^^
+   |
+help: try this
+   |
+LL |     writeln!(&mut v, "Hello {} world", world);
+   |                                ^^^^^       --
 
 error: literal with an empty format string
   --> $DIR/write_literal.rs:32:34
    |
 LL |     writeln!(&mut v, "Hello {}", "world");
    |                                  ^^^^^^^
+   |
+help: try this
+   |
+LL |     writeln!(&mut v, "Hello world");
+   |                             ^^^^^--
 
 error: literal with an empty format string
   --> $DIR/write_literal.rs:37:33
    |
 LL |     writeln!(&mut v, "{0} {1}", "hello", "world");
    |                                 ^^^^^^^
+   |
+help: try this
+   |
+LL |     writeln!(&mut v, "hello {1}", "world");
+   |                       ^^^^^    --
 
 error: literal with an empty format string
   --> $DIR/write_literal.rs:37:42
    |
 LL |     writeln!(&mut v, "{0} {1}", "hello", "world");
    |                                          ^^^^^^^
+   |
+help: try this
+   |
+LL |     writeln!(&mut v, "{0} world", "hello");
+   |                           ^^^^^         --
 
 error: literal with an empty format string
   --> $DIR/write_literal.rs:38:33
    |
 LL |     writeln!(&mut v, "{1} {0}", "hello", "world");
    |                                 ^^^^^^^
+   |
+help: try this
+   |
+LL |     writeln!(&mut v, "{1} hello", "world");
+   |                           ^^^^^--
 
 error: literal with an empty format string
   --> $DIR/write_literal.rs:38:42
    |
 LL |     writeln!(&mut v, "{1} {0}", "hello", "world");
    |                                          ^^^^^^^
+   |
+help: try this
+   |
+LL |     writeln!(&mut v, "world {0}", "hello");
+   |                       ^^^^^             --
 
 error: literal with an empty format string
-  --> $DIR/write_literal.rs:41:43
+  --> $DIR/write_literal.rs:41:37
    |
 LL |     writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world");
-   |                                           ^^^^^^^
+   |                                     ^^^^^^^^^^^^^
+   |
+help: try this
+   |
+LL |     writeln!(&mut v, "hello {bar}", bar = "world");
+   |                       ^^^^^      --
 
 error: literal with an empty format string
-  --> $DIR/write_literal.rs:41:58
+  --> $DIR/write_literal.rs:41:52
    |
 LL |     writeln!(&mut v, "{foo} {bar}", foo = "hello", bar = "world");
-   |                                                          ^^^^^^^
+   |                                                    ^^^^^^^^^^^^^
+   |
+help: try this
+   |
+LL |     writeln!(&mut v, "{foo} world", foo = "hello");
+   |                             ^^^^^               --
 
 error: literal with an empty format string
-  --> $DIR/write_literal.rs:42:43
+  --> $DIR/write_literal.rs:42:37
    |
 LL |     writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world");
-   |                                           ^^^^^^^
+   |                                     ^^^^^^^^^^^^^
+   |
+help: try this
+   |
+LL |     writeln!(&mut v, "{bar} hello", bar = "world");
+   |                             ^^^^^--
 
 error: literal with an empty format string
-  --> $DIR/write_literal.rs:42:58
+  --> $DIR/write_literal.rs:42:52
    |
 LL |     writeln!(&mut v, "{bar} {foo}", foo = "hello", bar = "world");
-   |                                                          ^^^^^^^
+   |                                                    ^^^^^^^^^^^^^
+   |
+help: try this
+   |
+LL |     writeln!(&mut v, "world {foo}", foo = "hello");
+   |                       ^^^^^                     --
 
 error: aborting due to 11 previous errors
 
diff --git a/tests/ui/write_literal_2.rs b/tests/ui/write_literal_2.rs
new file mode 100644 (file)
index 0000000..f341e82
--- /dev/null
@@ -0,0 +1,27 @@
+#![allow(unused_must_use)]
+#![warn(clippy::write_literal)]
+
+use std::io::Write;
+
+fn main() {
+    let mut v = Vec::new();
+
+    writeln!(&mut v, "{}", "{hello}");
+    writeln!(&mut v, r"{}", r"{hello}");
+    writeln!(&mut v, "{}", '\'');
+    writeln!(&mut v, "{}", '"');
+    writeln!(&mut v, r"{}", '"'); // don't lint
+    writeln!(&mut v, r"{}", '\'');
+    writeln!(
+        &mut v,
+        "some {}",
+        "hello \
+        world!"
+    );
+    writeln!(
+        &mut v,
+        "some {}\
+        {} \\ {}",
+        "1", "2", "3",
+    );
+}
diff --git a/tests/ui/write_literal_2.stderr b/tests/ui/write_literal_2.stderr
new file mode 100644 (file)
index 0000000..5b48835
--- /dev/null
@@ -0,0 +1,106 @@
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:9:28
+   |
+LL |     writeln!(&mut v, "{}", "{hello}");
+   |                            ^^^^^^^^^
+   |
+   = note: `-D clippy::write-literal` implied by `-D warnings`
+help: try this
+   |
+LL |     writeln!(&mut v, "{{hello}}");
+   |                       ^^^^^^^^^--
+
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:10:29
+   |
+LL |     writeln!(&mut v, r"{}", r"{hello}");
+   |                             ^^^^^^^^^^
+   |
+help: try this
+   |
+LL |     writeln!(&mut v, r"{{hello}}");
+   |                        ^^^^^^^^^--
+
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:11:28
+   |
+LL |     writeln!(&mut v, "{}", '/'');
+   |                            ^^^^
+   |
+help: try this
+   |
+LL |     writeln!(&mut v, "'");
+   |                       ^--
+
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:12:28
+   |
+LL |     writeln!(&mut v, "{}", '"');
+   |                            ^^^
+   |
+help: try this
+   |
+LL |     writeln!(&mut v, "/"");
+   |                       ^^--
+
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:14:29
+   |
+LL |     writeln!(&mut v, r"{}", '/'');
+   |                             ^^^^
+   |
+help: try this
+   |
+LL |     writeln!(&mut v, r"'");
+   |                        ^--
+
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:18:9
+   |
+LL | /         "hello /
+LL | |         world!"
+   | |_______________^
+   |
+help: try this
+   |
+LL |         "some hello /
+LL |         world!"
+   |
+
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:25:9
+   |
+LL |         "1", "2", "3",
+   |         ^^^
+   |
+help: try this
+   |
+LL |         "some 1{} / {}", "2", "3",
+   |               ^        --
+
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:25:14
+   |
+LL |         "1", "2", "3",
+   |              ^^^
+   |
+help: try this
+   |
+LL |         2 / {}",
+LL |         "1", "3",
+   |
+
+error: literal with an empty format string
+  --> $DIR/write_literal_2.rs:25:19
+   |
+LL |         "1", "2", "3",
+   |                   ^^^
+   |
+help: try this
+   |
+LL |         {} / 3",
+LL |         "1", "2",
+   |
+
+error: aborting due to 9 previous errors
+
index 6cfc0fcb4cae45edcd4f970215fa73898050358d..ba9e19a17220ff55d214e1628350d6bf10cfb7e3 100644 (file)
@@ -163,3 +163,35 @@ trait C: Copy {
         fn to_mut(&mut self);
     }
 }
+
+mod issue6727 {
+    trait ToU64 {
+        fn to_u64(self) -> u64;
+        fn to_u64_v2(&self) -> u64;
+    }
+
+    #[derive(Clone, Copy)]
+    struct FooCopy;
+
+    impl ToU64 for FooCopy {
+        fn to_u64(self) -> u64 {
+            1
+        }
+        // trigger lint
+        fn to_u64_v2(&self) -> u64 {
+            1
+        }
+    }
+
+    struct FooNoCopy;
+
+    impl ToU64 for FooNoCopy {
+        // trigger lint
+        fn to_u64(self) -> u64 {
+            2
+        }
+        fn to_u64_v2(&self) -> u64 {
+            2
+        }
+    }
+}
index 32bd9075bd5e14c0a399055c6deeaab65f218c45..1d58a12ac795b9706fd8bf1fef7c67adf4d3b990 100644 (file)
-error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
+error: methods called `from_*` usually take no `self`
   --> $DIR/wrong_self_convention.rs:18:17
    |
 LL |     fn from_i32(self) {}
    |                 ^^^^
    |
    = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
+   = help: consider choosing a less ambiguous name
 
-error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
+error: methods called `from_*` usually take no `self`
   --> $DIR/wrong_self_convention.rs:24:21
    |
 LL |     pub fn from_i64(self) {}
    |                     ^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
   --> $DIR/wrong_self_convention.rs:36:15
    |
 LL |     fn as_i32(self) {}
    |               ^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
+error: methods called `into_*` usually take `self` by value
   --> $DIR/wrong_self_convention.rs:38:17
    |
 LL |     fn into_i32(&self) {}
    |                 ^^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name
+error: methods called `is_*` usually take `self` by reference or no `self`
   --> $DIR/wrong_self_convention.rs:40:15
    |
 LL |     fn is_i32(self) {}
    |               ^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name
+error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
   --> $DIR/wrong_self_convention.rs:42:15
    |
 LL |     fn to_i32(self) {}
    |               ^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
+error: methods called `from_*` usually take no `self`
   --> $DIR/wrong_self_convention.rs:44:17
    |
 LL |     fn from_i32(self) {}
    |                 ^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
   --> $DIR/wrong_self_convention.rs:46:19
    |
 LL |     pub fn as_i64(self) {}
    |                   ^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
+error: methods called `into_*` usually take `self` by value
   --> $DIR/wrong_self_convention.rs:47:21
    |
 LL |     pub fn into_i64(&self) {}
    |                     ^^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name
+error: methods called `is_*` usually take `self` by reference or no `self`
   --> $DIR/wrong_self_convention.rs:48:19
    |
 LL |     pub fn is_i64(self) {}
    |                   ^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name
+error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
   --> $DIR/wrong_self_convention.rs:49:19
    |
 LL |     pub fn to_i64(self) {}
    |                   ^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
+error: methods called `from_*` usually take no `self`
   --> $DIR/wrong_self_convention.rs:50:21
    |
 LL |     pub fn from_i64(self) {}
    |                     ^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
   --> $DIR/wrong_self_convention.rs:95:19
    |
 LL |         fn as_i32(self) {}
    |                   ^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
+error: methods called `into_*` usually take `self` by value
   --> $DIR/wrong_self_convention.rs:98:25
    |
 LL |         fn into_i32_ref(&self) {}
    |                         ^^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name
+error: methods called `is_*` usually take `self` by reference or no `self`
   --> $DIR/wrong_self_convention.rs:100:19
    |
 LL |         fn is_i32(self) {}
    |                   ^^^^
-
-error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name
-  --> $DIR/wrong_self_convention.rs:102:19
    |
-LL |         fn to_i32(self) {}
-   |                   ^^^^
+   = help: consider choosing a less ambiguous name
 
-error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
+error: methods called `from_*` usually take no `self`
   --> $DIR/wrong_self_convention.rs:104:21
    |
 LL |         fn from_i32(self) {}
    |                     ^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `as_*` usually take self by reference or self by mutable reference; consider choosing a less ambiguous name
+error: methods called `as_*` usually take `self` by reference or `self` by mutable reference
   --> $DIR/wrong_self_convention.rs:119:19
    |
 LL |         fn as_i32(self);
    |                   ^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
+error: methods called `into_*` usually take `self` by value
   --> $DIR/wrong_self_convention.rs:122:25
    |
 LL |         fn into_i32_ref(&self);
    |                         ^^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `is_*` usually take self by reference or no self; consider choosing a less ambiguous name
+error: methods called `is_*` usually take `self` by reference or no `self`
   --> $DIR/wrong_self_convention.rs:124:19
    |
 LL |         fn is_i32(self);
    |                   ^^^^
-
-error: methods called `to_*` usually take self by reference; consider choosing a less ambiguous name
-  --> $DIR/wrong_self_convention.rs:126:19
    |
-LL |         fn to_i32(self);
-   |                   ^^^^
+   = help: consider choosing a less ambiguous name
 
-error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
+error: methods called `from_*` usually take no `self`
   --> $DIR/wrong_self_convention.rs:128:21
    |
 LL |         fn from_i32(self);
    |                     ^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `into_*` usually take self by value; consider choosing a less ambiguous name
+error: methods called `into_*` usually take `self` by value
   --> $DIR/wrong_self_convention.rs:146:25
    |
 LL |         fn into_i32_ref(&self);
    |                         ^^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
-error: methods called `from_*` usually take no self; consider choosing a less ambiguous name
+error: methods called `from_*` usually take no `self`
   --> $DIR/wrong_self_convention.rs:152:21
    |
 LL |         fn from_i32(self);
    |                     ^^^^
+   |
+   = help: consider choosing a less ambiguous name
+
+error: methods with the following characteristics: (`to_*` and `self` type is `Copy`) usually take `self` by value
+  --> $DIR/wrong_self_convention.rs:181:22
+   |
+LL |         fn to_u64_v2(&self) -> u64 {
+   |                      ^^^^^
+   |
+   = help: consider choosing a less ambiguous name
+
+error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
+  --> $DIR/wrong_self_convention.rs:190:19
+   |
+LL |         fn to_u64(self) -> u64 {
+   |                   ^^^^
+   |
+   = help: consider choosing a less ambiguous name
 
 error: aborting due to 24 previous errors
 
diff --git a/tests/ui/wrong_self_conventions_mut.rs b/tests/ui/wrong_self_conventions_mut.rs
new file mode 100644 (file)
index 0000000..486a0d7
--- /dev/null
@@ -0,0 +1,30 @@
+// edition:2018
+#![warn(clippy::wrong_self_convention)]
+#![allow(dead_code)]
+
+fn main() {}
+
+mod issue6758 {
+    pub enum Test<T> {
+        One(T),
+        Many(Vec<T>),
+    }
+
+    impl<T> Test<T> {
+        // If a method starts with `to_` and not ends with `_mut` it should expect `&self`
+        pub fn to_many(&mut self) -> Option<&mut [T]> {
+            match self {
+                Self::Many(data) => Some(data),
+                _ => None,
+            }
+        }
+
+        // If a method starts with `to_` and ends with `_mut` it should expect `&mut self`
+        pub fn to_many_mut(&self) -> Option<&[T]> {
+            match self {
+                Self::Many(data) => Some(data),
+                _ => None,
+            }
+        }
+    }
+}
diff --git a/tests/ui/wrong_self_conventions_mut.stderr b/tests/ui/wrong_self_conventions_mut.stderr
new file mode 100644 (file)
index 0000000..6ce37c5
--- /dev/null
@@ -0,0 +1,19 @@
+error: methods with the following characteristics: (`to_*` and `self` type is not `Copy`) usually take `self` by reference
+  --> $DIR/wrong_self_conventions_mut.rs:15:24
+   |
+LL |         pub fn to_many(&mut self) -> Option<&mut [T]> {
+   |                        ^^^^^^^^^
+   |
+   = note: `-D clippy::wrong-self-convention` implied by `-D warnings`
+   = help: consider choosing a less ambiguous name
+
+error: methods with the following characteristics: (`to_*` and `*_mut`) usually take `self` by mutable reference
+  --> $DIR/wrong_self_conventions_mut.rs:23:28
+   |
+LL |         pub fn to_many_mut(&self) -> Option<&[T]> {
+   |                            ^^^^^
+   |
+   = help: consider choosing a less ambiguous name
+
+error: aborting due to 2 previous errors
+